diff options
Diffstat (limited to 'coreutils/od_bloaty.c')
-rw-r--r-- | coreutils/od_bloaty.c | 443 |
1 files changed, 199 insertions, 244 deletions
diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c index 4c6b64d5e..8013f483c 100644 --- a/coreutils/od_bloaty.c +++ b/coreutils/od_bloaty.c | |||
@@ -13,48 +13,64 @@ | |||
13 | 13 | ||
14 | You should have received a copy of the GNU General Public License | 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, | 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. */ | 16 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
17 | 17 | */ | |
18 | /* Written by Jim Meyering. */ | 18 | /* Written by Jim Meyering. */ |
19 | /* Busyboxed by Denys Vlasenko, based on od.c from coreutils-5.2.1 */ | ||
19 | 20 | ||
20 | /* Busyboxed by Denys Vlasenko | ||
21 | |||
22 | Based on od.c from coreutils-5.2.1 | ||
23 | Top bloat sources: | ||
24 | 21 | ||
25 | 00000073 t parse_old_offset | 22 | /* #include "libbb.h" - done in od.c */ |
26 | 0000007b t get_lcm | 23 | #define assert(a) ((void)0) |
27 | 00000090 r long_options | ||
28 | 00000092 t print_named_ascii | ||
29 | 000000bf t print_ascii | ||
30 | 00000168 t write_block | ||
31 | 00000366 t decode_format_string | ||
32 | 00000a71 T od_main | ||
33 | |||
34 | Tested for compat with coreutils 6.3 | ||
35 | using this script. Minor differences fixed. | ||
36 | 24 | ||
37 | #!/bin/sh | ||
38 | echo STD | ||
39 | time /path/to/coreutils/od \ | ||
40 | ...params... \ | ||
41 | >std | ||
42 | echo Exit code $? | ||
43 | echo BBOX | ||
44 | time ./busybox od \ | ||
45 | ...params... \ | ||
46 | >bbox | ||
47 | echo Exit code $? | ||
48 | diff -u -a std bbox >bbox.diff || { echo Different!; sleep 1; } | ||
49 | 25 | ||
50 | */ | 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 | |||
38 | enum { | ||
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 | }; | ||
51 | 62 | ||
52 | #include "libbb.h" | 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) | ||
53 | 70 | ||
54 | #define assert(a) ((void)0) | ||
55 | 71 | ||
56 | /* Check for 0x7f is a coreutils 6.3 addition */ | 72 | /* Check for 0x7f is a coreutils 6.3 addition */ |
57 | #define ISPRINT(c) (((c)>=' ') && (c) != 0x7f) | 73 | #define ISPRINT(c) (((c) >= ' ') && (c) < 0x7f) |
58 | 74 | ||
59 | typedef long double longdouble_t; | 75 | typedef long double longdouble_t; |
60 | typedef unsigned long long ulonglong_t; | 76 | typedef unsigned long long ulonglong_t; |
@@ -165,17 +181,9 @@ struct ERR_width_bytes_has_bad_size { | |||
165 | char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1]; | 181 | char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1]; |
166 | }; | 182 | }; |
167 | 183 | ||
168 | static smallint flag_dump_strings; | 184 | static smallint exit_code; |
169 | /* Non-zero if an old-style 'pseudo-address' was specified. */ | ||
170 | static smallint flag_pseudo_start; | ||
171 | static smallint limit_bytes_to_format; | ||
172 | /* When zero and two or more consecutive blocks are equal, format | ||
173 | only the first block and output an asterisk alone on the following | ||
174 | line to indicate that identical blocks have been elided. */ | ||
175 | static smallint verbose; | ||
176 | static smallint ioerror; | ||
177 | 185 | ||
178 | static size_t string_min; | 186 | static unsigned string_min; |
179 | 187 | ||
180 | /* An array of specs describing how to format each input block. */ | 188 | /* An array of specs describing how to format each input block. */ |
181 | static size_t n_specs; | 189 | static size_t n_specs; |
@@ -186,7 +194,11 @@ static struct tspec *spec; | |||
186 | static void (*format_address)(off_t, char); | 194 | static void (*format_address)(off_t, char); |
187 | /* The difference between the old-style pseudo starting address and | 195 | /* The difference between the old-style pseudo starting address and |
188 | the number of bytes to skip. */ | 196 | the number of bytes to skip. */ |
197 | #if ENABLE_LONG_OPTS | ||
189 | static off_t pseudo_offset; | 198 | static off_t pseudo_offset; |
199 | #else | ||
200 | enum { pseudo_offset = 0 }; | ||
201 | #endif | ||
190 | /* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all | 202 | /* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all |
191 | input is formatted. */ | 203 | input is formatted. */ |
192 | 204 | ||
@@ -221,7 +233,7 @@ static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 | |||
221 | 233 | ||
222 | #define MAX_FP_TYPE_SIZE sizeof(longdouble_t) | 234 | #define MAX_FP_TYPE_SIZE sizeof(longdouble_t) |
223 | static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = { | 235 | static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = { |
224 | /* gcc seems to allow repeated indexes. Last one stays */ | 236 | /* gcc seems to allow repeated indexes. Last one wins */ |
225 | [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE, | 237 | [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE, |
226 | [sizeof(double)] = FLOAT_DOUBLE, | 238 | [sizeof(double)] = FLOAT_DOUBLE, |
227 | [sizeof(float)] = FLOAT_SINGLE | 239 | [sizeof(float)] = FLOAT_SINGLE |
@@ -383,7 +395,7 @@ print_named_ascii(size_t n_bytes, const char *block, | |||
383 | }; | 395 | }; |
384 | // buf[N] pos: 01234 56789 | 396 | // buf[N] pos: 01234 56789 |
385 | char buf[12] = " x\0 0xx\0"; | 397 | char buf[12] = " x\0 0xx\0"; |
386 | // actually " x\0 xxx\0", but I want to share the string with below. | 398 | // actually " x\0 xxx\0", but want to share string with print_ascii. |
387 | // [12] because we take three 32bit stack slots anyway, and | 399 | // [12] because we take three 32bit stack slots anyway, and |
388 | // gcc is too dumb to initialize with constant stores, | 400 | // gcc is too dumb to initialize with constant stores, |
389 | // it copies initializer from rodata. Oh well. | 401 | // it copies initializer from rodata. Oh well. |
@@ -479,10 +491,10 @@ open_next_file(void) | |||
479 | if (in_stream) { | 491 | if (in_stream) { |
480 | break; | 492 | break; |
481 | } | 493 | } |
482 | ioerror = 1; | 494 | exit_code = 1; |
483 | } | 495 | } |
484 | 496 | ||
485 | if (limit_bytes_to_format && !flag_dump_strings) | 497 | if ((option_mask32 & (OPT_N|OPT_S)) == OPT_N) |
486 | setbuf(in_stream, NULL); | 498 | setbuf(in_stream, NULL); |
487 | } | 499 | } |
488 | 500 | ||
@@ -502,15 +514,14 @@ check_and_close(void) | |||
502 | ? bb_msg_standard_input | 514 | ? bb_msg_standard_input |
503 | : file_list[-1] | 515 | : file_list[-1] |
504 | ); | 516 | ); |
505 | ioerror = 1; | 517 | exit_code = 1; |
506 | } | 518 | } |
507 | fclose_if_not_stdin(in_stream); | 519 | fclose_if_not_stdin(in_stream); |
508 | in_stream = NULL; | 520 | in_stream = NULL; |
509 | } | 521 | } |
510 | 522 | ||
511 | if (ferror(stdout)) { | 523 | if (ferror(stdout)) { |
512 | bb_error_msg(bb_msg_write_error); | 524 | bb_error_msg_and_die(bb_msg_write_error); |
513 | ioerror = 1; | ||
514 | } | 525 | } |
515 | } | 526 | } |
516 | 527 | ||
@@ -542,7 +553,6 @@ decode_one_format(const char *s_orig, const char *s, struct tspec *tspec) | |||
542 | unsigned field_width = 0; | 553 | unsigned field_width = 0; |
543 | int pos; | 554 | int pos; |
544 | 555 | ||
545 | |||
546 | switch (*s) { | 556 | switch (*s) { |
547 | case 'd': | 557 | case 'd': |
548 | case 'o': | 558 | case 'o': |
@@ -788,7 +798,7 @@ skip(off_t n_skip) | |||
788 | /* take "check & close / open_next" route */ | 798 | /* take "check & close / open_next" route */ |
789 | } else { | 799 | } else { |
790 | if (fseeko(in_stream, n_skip, SEEK_CUR) != 0) | 800 | if (fseeko(in_stream, n_skip, SEEK_CUR) != 0) |
791 | ioerror = 1; | 801 | exit_code = 1; |
792 | return; | 802 | return; |
793 | } | 803 | } |
794 | } else { | 804 | } else { |
@@ -889,7 +899,8 @@ write_block(off_t current_offset, size_t n_bytes, | |||
889 | static char prev_pair_equal = 0; | 899 | static char prev_pair_equal = 0; |
890 | size_t i; | 900 | size_t i; |
891 | 901 | ||
892 | if (!verbose && !first | 902 | if (!(option_mask32 & OPT_v) |
903 | && !first | ||
893 | && n_bytes == bytes_per_block | 904 | && n_bytes == bytes_per_block |
894 | && memcmp(prev_block, curr_block, bytes_per_block) == 0 | 905 | && memcmp(prev_block, curr_block, bytes_per_block) == 0 |
895 | ) { | 906 | ) { |
@@ -911,9 +922,9 @@ write_block(off_t current_offset, size_t n_bytes, | |||
911 | (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string); | 922 | (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string); |
912 | if (spec[i].hexl_mode_trailer) { | 923 | if (spec[i].hexl_mode_trailer) { |
913 | /* space-pad out to full line width, then dump the trailer */ | 924 | /* space-pad out to full line width, then dump the trailer */ |
914 | int datum_width = width_bytes[spec[i].size]; | 925 | unsigned datum_width = width_bytes[spec[i].size]; |
915 | int blank_fields = (bytes_per_block - n_bytes) / datum_width; | 926 | unsigned blank_fields = (bytes_per_block - n_bytes) / datum_width; |
916 | int field_width = spec[i].field_width + 1; | 927 | unsigned field_width = spec[i].field_width + 1; |
917 | printf("%*s", blank_fields * field_width, ""); | 928 | printf("%*s", blank_fields * field_width, ""); |
918 | dump_hexl_mode_trailer(n_bytes, curr_block); | 929 | dump_hexl_mode_trailer(n_bytes, curr_block); |
919 | } | 930 | } |
@@ -961,42 +972,6 @@ get_lcm(void) | |||
961 | return l_c_m; | 972 | return l_c_m; |
962 | } | 973 | } |
963 | 974 | ||
964 | #if ENABLE_LONG_OPTS | ||
965 | /* If S is a valid traditional offset specification with an optional | ||
966 | leading '+' return nonzero and set *OFFSET to the offset it denotes. */ | ||
967 | |||
968 | static int | ||
969 | parse_old_offset(const char *s, off_t *offset) | ||
970 | { | ||
971 | static const struct suffix_mult Bb[] = { | ||
972 | { "B", 1024 }, | ||
973 | { "b", 512 }, | ||
974 | { "", 0 } | ||
975 | }; | ||
976 | char *p; | ||
977 | int radix; | ||
978 | |||
979 | /* Skip over any leading '+'. */ | ||
980 | if (s[0] == '+') ++s; | ||
981 | |||
982 | /* Determine the radix we'll use to interpret S. If there is a '.', | ||
983 | * it's decimal, otherwise, if the string begins with '0X'or '0x', | ||
984 | * it's hexadecimal, else octal. */ | ||
985 | p = strchr(s, '.'); | ||
986 | radix = 8; | ||
987 | if (p) { | ||
988 | p[0] = '\0'; /* cheating */ | ||
989 | radix = 10; | ||
990 | } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) | ||
991 | radix = 16; | ||
992 | |||
993 | *offset = xstrtooff_sfx(s, radix, Bb); | ||
994 | if (p) p[0] = '.'; | ||
995 | |||
996 | return (*offset >= 0); | ||
997 | } | ||
998 | #endif | ||
999 | |||
1000 | /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the | 975 | /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the |
1001 | formatted block to standard output, and repeat until the specified | 976 | formatted block to standard output, and repeat until the specified |
1002 | maximum number of bytes has been read or until all input has been | 977 | maximum number of bytes has been read or until all input has been |
@@ -1014,27 +989,25 @@ dump(off_t current_offset, off_t end_offset) | |||
1014 | int idx; | 989 | int idx; |
1015 | size_t n_bytes_read; | 990 | size_t n_bytes_read; |
1016 | 991 | ||
1017 | block[0] = xmalloc(2*bytes_per_block); | 992 | block[0] = xmalloc(2 * bytes_per_block); |
1018 | block[1] = block[0] + bytes_per_block; | 993 | block[1] = block[0] + bytes_per_block; |
1019 | 994 | ||
1020 | idx = 0; | 995 | idx = 0; |
1021 | if (limit_bytes_to_format) { | 996 | if (option_mask32 & OPT_N) { |
1022 | while (1) { | 997 | while (1) { |
1023 | size_t n_needed; | 998 | size_t n_needed; |
1024 | if (current_offset >= end_offset) { | 999 | if (current_offset >= end_offset) { |
1025 | n_bytes_read = 0; | 1000 | n_bytes_read = 0; |
1026 | break; | 1001 | break; |
1027 | } | 1002 | } |
1028 | n_needed = MIN(end_offset - current_offset, | 1003 | n_needed = MIN(end_offset - current_offset, (off_t) bytes_per_block); |
1029 | (off_t) bytes_per_block); | ||
1030 | read_block(n_needed, block[idx], &n_bytes_read); | 1004 | read_block(n_needed, block[idx], &n_bytes_read); |
1031 | if (n_bytes_read < bytes_per_block) | 1005 | if (n_bytes_read < bytes_per_block) |
1032 | break; | 1006 | break; |
1033 | assert(n_bytes_read == bytes_per_block); | 1007 | assert(n_bytes_read == bytes_per_block); |
1034 | write_block(current_offset, n_bytes_read, | 1008 | write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]); |
1035 | block[!idx], block[idx]); | ||
1036 | current_offset += n_bytes_read; | 1009 | current_offset += n_bytes_read; |
1037 | idx = !idx; | 1010 | idx ^= 1; |
1038 | } | 1011 | } |
1039 | } else { | 1012 | } else { |
1040 | while (1) { | 1013 | while (1) { |
@@ -1042,10 +1015,9 @@ dump(off_t current_offset, off_t end_offset) | |||
1042 | if (n_bytes_read < bytes_per_block) | 1015 | if (n_bytes_read < bytes_per_block) |
1043 | break; | 1016 | break; |
1044 | assert(n_bytes_read == bytes_per_block); | 1017 | assert(n_bytes_read == bytes_per_block); |
1045 | write_block(current_offset, n_bytes_read, | 1018 | write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]); |
1046 | block[!idx], block[idx]); | ||
1047 | current_offset += n_bytes_read; | 1019 | current_offset += n_bytes_read; |
1048 | idx = !idx; | 1020 | idx ^= 1; |
1049 | } | 1021 | } |
1050 | } | 1022 | } |
1051 | 1023 | ||
@@ -1061,41 +1033,18 @@ dump(off_t current_offset, off_t end_offset) | |||
1061 | 1033 | ||
1062 | memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read); | 1034 | memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read); |
1063 | write_block(current_offset, bytes_to_write, | 1035 | write_block(current_offset, bytes_to_write, |
1064 | block[!idx], block[idx]); | 1036 | block[idx ^ 1], block[idx]); |
1065 | current_offset += n_bytes_read; | 1037 | current_offset += n_bytes_read; |
1066 | } | 1038 | } |
1067 | 1039 | ||
1068 | format_address(current_offset, '\n'); | 1040 | format_address(current_offset, '\n'); |
1069 | 1041 | ||
1070 | if (limit_bytes_to_format && current_offset >= end_offset) | 1042 | if ((option_mask32 & OPT_N) && current_offset >= end_offset) |
1071 | check_and_close(); | 1043 | check_and_close(); |
1072 | 1044 | ||
1073 | free(block[0]); | 1045 | free(block[0]); |
1074 | } | 1046 | } |
1075 | 1047 | ||
1076 | /* Read a single byte into *C from the concatenation of the input files | ||
1077 | named in the global array FILE_LIST. On the first call to this | ||
1078 | function, the global variable IN_STREAM is expected to be an open | ||
1079 | stream associated with the input file INPUT_FILENAME. If IN_STREAM | ||
1080 | is at end-of-file, close it and update the global variables IN_STREAM | ||
1081 | and INPUT_FILENAME so they correspond to the next file in the list. | ||
1082 | Then try to read a byte from the newly opened file. Repeat if | ||
1083 | necessary until EOF is reached for the last file in FILE_LIST, then | ||
1084 | set *C to EOF and return. Subsequent calls do likewise. */ | ||
1085 | |||
1086 | static void | ||
1087 | read_char(int *c) | ||
1088 | { | ||
1089 | while (in_stream) { /* !EOF */ | ||
1090 | *c = fgetc(in_stream); | ||
1091 | if (*c != EOF) | ||
1092 | return; | ||
1093 | check_and_close(); | ||
1094 | open_next_file(); | ||
1095 | } | ||
1096 | *c = EOF; | ||
1097 | } | ||
1098 | |||
1099 | /* Read N bytes into BLOCK from the concatenation of the input files | 1048 | /* Read N bytes into BLOCK from the concatenation of the input files |
1100 | named in the global array FILE_LIST. On the first call to this | 1049 | named in the global array FILE_LIST. On the first call to this |
1101 | function, the global variable IN_STREAM is expected to be an open | 1050 | function, the global variable IN_STREAM is expected to be an open |
@@ -1119,8 +1068,8 @@ read_char(int *c) | |||
1119 | static void | 1068 | static void |
1120 | dump_strings(off_t address, off_t end_offset) | 1069 | dump_strings(off_t address, off_t end_offset) |
1121 | { | 1070 | { |
1122 | size_t bufsize = MAX(100, string_min); | 1071 | unsigned bufsize = MAX(100, string_min); |
1123 | char *buf = xmalloc(bufsize); | 1072 | unsigned char *buf = xmalloc(bufsize); |
1124 | 1073 | ||
1125 | while (1) { | 1074 | while (1) { |
1126 | size_t i; | 1075 | size_t i; |
@@ -1128,19 +1077,25 @@ dump_strings(off_t address, off_t end_offset) | |||
1128 | 1077 | ||
1129 | /* See if the next 'string_min' chars are all printing chars. */ | 1078 | /* See if the next 'string_min' chars are all printing chars. */ |
1130 | tryline: | 1079 | tryline: |
1131 | if (limit_bytes_to_format && (end_offset - string_min <= address)) | 1080 | if ((option_mask32 & OPT_N) && (end_offset - string_min <= address)) |
1132 | break; | 1081 | break; |
1133 | i = 0; | 1082 | i = 0; |
1134 | while (!limit_bytes_to_format || address < end_offset) { | 1083 | while (!(option_mask32 & OPT_N) || address < end_offset) { |
1135 | if (i == bufsize) { | 1084 | if (i == bufsize) { |
1136 | bufsize += bufsize/8; | 1085 | bufsize += bufsize/8; |
1137 | buf = xrealloc(buf, bufsize); | 1086 | buf = xrealloc(buf, bufsize); |
1138 | } | 1087 | } |
1139 | read_char(&c); | 1088 | |
1140 | if (c < 0) { /* EOF */ | 1089 | while (in_stream) { /* !EOF */ |
1141 | free(buf); | 1090 | c = fgetc(in_stream); |
1142 | return; | 1091 | if (c != EOF) |
1092 | goto got_char; | ||
1093 | check_and_close(); | ||
1094 | open_next_file(); | ||
1143 | } | 1095 | } |
1096 | /* EOF */ | ||
1097 | goto ret; | ||
1098 | got_char: | ||
1144 | address++; | 1099 | address++; |
1145 | if (!c) | 1100 | if (!c) |
1146 | break; | 1101 | break; |
@@ -1152,8 +1107,7 @@ dump_strings(off_t address, off_t end_offset) | |||
1152 | if (i < string_min) /* Too short! */ | 1107 | if (i < string_min) /* Too short! */ |
1153 | goto tryline; | 1108 | goto tryline; |
1154 | 1109 | ||
1155 | /* If we get here, the string is all printable and NUL-terminated, | 1110 | /* If we get here, the string is all printable and NUL-terminated */ |
1156 | * so print it. It is all in 'buf' and 'i' is its length. */ | ||
1157 | buf[i] = 0; | 1111 | buf[i] = 0; |
1158 | format_address(address - i - 1, ' '); | 1112 | format_address(address - i - 1, ' '); |
1159 | 1113 | ||
@@ -1174,13 +1128,50 @@ dump_strings(off_t address, off_t end_offset) | |||
1174 | 1128 | ||
1175 | /* We reach this point only if we search through | 1129 | /* We reach this point only if we search through |
1176 | (max_bytes_to_format - string_min) bytes before reaching EOF. */ | 1130 | (max_bytes_to_format - string_min) bytes before reaching EOF. */ |
1131 | check_and_close(); | ||
1132 | ret: | ||
1177 | free(buf); | 1133 | free(buf); |
1134 | } | ||
1178 | 1135 | ||
1179 | check_and_close(); | 1136 | #if ENABLE_LONG_OPTS |
1137 | /* If S is a valid traditional offset specification with an optional | ||
1138 | leading '+' return nonzero and set *OFFSET to the offset it denotes. */ | ||
1139 | |||
1140 | static int | ||
1141 | parse_old_offset(const char *s, off_t *offset) | ||
1142 | { | ||
1143 | static const struct suffix_mult Bb[] = { | ||
1144 | { "B", 1024 }, | ||
1145 | { "b", 512 }, | ||
1146 | { "", 0 } | ||
1147 | }; | ||
1148 | char *p; | ||
1149 | int radix; | ||
1150 | |||
1151 | /* Skip over any leading '+'. */ | ||
1152 | if (s[0] == '+') ++s; | ||
1153 | if (!isdigit(s[0])) return 0; /* not a number */ | ||
1154 | |||
1155 | /* Determine the radix we'll use to interpret S. If there is a '.', | ||
1156 | * it's decimal, otherwise, if the string begins with '0X'or '0x', | ||
1157 | * it's hexadecimal, else octal. */ | ||
1158 | p = strchr(s, '.'); | ||
1159 | radix = 8; | ||
1160 | if (p) { | ||
1161 | p[0] = '\0'; /* cheating */ | ||
1162 | radix = 10; | ||
1163 | } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) | ||
1164 | radix = 16; | ||
1165 | |||
1166 | *offset = xstrtooff_sfx(s, radix, Bb); | ||
1167 | if (p) p[0] = '.'; | ||
1168 | |||
1169 | return (*offset >= 0); | ||
1180 | } | 1170 | } |
1171 | #endif | ||
1181 | 1172 | ||
1182 | int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 1173 | int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1183 | int od_main(int argc, char **argv) | 1174 | int od_main(int argc UNUSED_PARAM, char **argv) |
1184 | { | 1175 | { |
1185 | static const struct suffix_mult bkm[] = { | 1176 | static const struct suffix_mult bkm[] = { |
1186 | { "b", 512 }, | 1177 | { "b", 512 }, |
@@ -1188,27 +1179,6 @@ int od_main(int argc, char **argv) | |||
1188 | { "m", 1024*1024 }, | 1179 | { "m", 1024*1024 }, |
1189 | { "", 0 } | 1180 | { "", 0 } |
1190 | }; | 1181 | }; |
1191 | enum { | ||
1192 | OPT_A = 1 << 0, | ||
1193 | OPT_N = 1 << 1, | ||
1194 | OPT_a = 1 << 2, | ||
1195 | OPT_b = 1 << 3, | ||
1196 | OPT_c = 1 << 4, | ||
1197 | OPT_d = 1 << 5, | ||
1198 | OPT_f = 1 << 6, | ||
1199 | OPT_h = 1 << 7, | ||
1200 | OPT_i = 1 << 8, | ||
1201 | OPT_j = 1 << 9, | ||
1202 | OPT_l = 1 << 10, | ||
1203 | OPT_o = 1 << 11, | ||
1204 | OPT_t = 1 << 12, | ||
1205 | OPT_v = 1 << 13, | ||
1206 | OPT_x = 1 << 14, | ||
1207 | OPT_s = 1 << 15, | ||
1208 | OPT_S = 1 << 16, | ||
1209 | OPT_w = 1 << 17, | ||
1210 | OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS, | ||
1211 | }; | ||
1212 | #if ENABLE_LONG_OPTS | 1182 | #if ENABLE_LONG_OPTS |
1213 | static const char od_longopts[] ALIGN1 = | 1183 | static const char od_longopts[] ALIGN1 = |
1214 | "skip-bytes\0" Required_argument "j" | 1184 | "skip-bytes\0" Required_argument "j" |
@@ -1216,18 +1186,18 @@ int od_main(int argc, char **argv) | |||
1216 | "read-bytes\0" Required_argument "N" | 1186 | "read-bytes\0" Required_argument "N" |
1217 | "format\0" Required_argument "t" | 1187 | "format\0" Required_argument "t" |
1218 | "output-duplicates\0" No_argument "v" | 1188 | "output-duplicates\0" No_argument "v" |
1189 | /* Yes, it's true: -S NUM, but --strings[=NUM]! | ||
1190 | * that is, NUM is mandatory for -S but optional for --strings! | ||
1191 | */ | ||
1219 | "strings\0" Optional_argument "S" | 1192 | "strings\0" Optional_argument "S" |
1220 | "width\0" Optional_argument "w" | 1193 | "width\0" Optional_argument "w" |
1221 | "traditional\0" No_argument "\xff" | 1194 | "traditional\0" No_argument "\xff" |
1222 | ; | 1195 | ; |
1223 | #endif | 1196 | #endif |
1224 | char *str_A, *str_N, *str_j, *str_S; | 1197 | const char *str_A, *str_N, *str_j, *str_S = "3"; |
1225 | llist_t *lst_t = NULL; | 1198 | llist_t *lst_t = NULL; |
1226 | unsigned opt; | 1199 | unsigned opt; |
1227 | int l_c_m; | 1200 | int l_c_m; |
1228 | /* The old-style 'pseudo starting address' to be printed in parentheses | ||
1229 | after any true address. */ | ||
1230 | off_t pseudo_start = pseudo_start; // for gcc | ||
1231 | /* The number of input bytes to skip before formatting and writing. */ | 1201 | /* The number of input bytes to skip before formatting and writing. */ |
1232 | off_t n_bytes_to_skip = 0; | 1202 | off_t n_bytes_to_skip = 0; |
1233 | /* The offset of the first byte after the last byte to be formatted. */ | 1203 | /* The offset of the first byte after the last byte to be formatted. */ |
@@ -1239,20 +1209,13 @@ int od_main(int argc, char **argv) | |||
1239 | format_address = format_address_std; | 1209 | format_address = format_address_std; |
1240 | address_base_char = 'o'; | 1210 | address_base_char = 'o'; |
1241 | address_pad_len_char = '7'; | 1211 | address_pad_len_char = '7'; |
1242 | /* flag_dump_strings = 0; - already is */ | ||
1243 | 1212 | ||
1244 | /* Parse command line */ | 1213 | /* Parse command line */ |
1245 | opt_complementary = "w+:t::"; /* -w N, -t is a list */ | 1214 | opt_complementary = "w+:t::"; /* -w N, -t is a list */ |
1246 | #if ENABLE_LONG_OPTS | 1215 | #if ENABLE_LONG_OPTS |
1247 | applet_long_options = od_longopts; | 1216 | applet_long_options = od_longopts; |
1248 | #endif | 1217 | #endif |
1249 | opt = getopt32(argv, "A:N:abcdfhij:lot:vxsS:" | 1218 | opt = OD_GETOPT32(); |
1250 | "w::", // -w with optional param | ||
1251 | // -S was -s and also had optional parameter | ||
1252 | // but in coreutils 6.3 it was renamed and now has | ||
1253 | // _mandatory_ parameter | ||
1254 | &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block); | ||
1255 | argc -= optind; | ||
1256 | argv += optind; | 1219 | argv += optind; |
1257 | if (opt & OPT_A) { | 1220 | if (opt & OPT_A) { |
1258 | static const char doxn[] ALIGN1 = "doxn"; | 1221 | static const char doxn[] ALIGN1 = "doxn"; |
@@ -1274,7 +1237,6 @@ int od_main(int argc, char **argv) | |||
1274 | address_pad_len_char = doxn_address_pad_len_char[pos]; | 1237 | address_pad_len_char = doxn_address_pad_len_char[pos]; |
1275 | } | 1238 | } |
1276 | if (opt & OPT_N) { | 1239 | if (opt & OPT_N) { |
1277 | limit_bytes_to_format = 1; | ||
1278 | max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm); | 1240 | max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm); |
1279 | } | 1241 | } |
1280 | if (opt & OPT_a) decode_format_string("a"); | 1242 | if (opt & OPT_a) decode_format_string("a"); |
@@ -1287,28 +1249,23 @@ int od_main(int argc, char **argv) | |||
1287 | if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm); | 1249 | if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm); |
1288 | if (opt & OPT_l) decode_format_string("d4"); | 1250 | if (opt & OPT_l) decode_format_string("d4"); |
1289 | if (opt & OPT_o) decode_format_string("o2"); | 1251 | if (opt & OPT_o) decode_format_string("o2"); |
1290 | //if (opt & OPT_t)... | ||
1291 | while (lst_t) { | 1252 | while (lst_t) { |
1292 | decode_format_string(llist_pop(&lst_t)); | 1253 | decode_format_string(llist_pop(&lst_t)); |
1293 | } | 1254 | } |
1294 | if (opt & OPT_v) verbose = 1; | ||
1295 | if (opt & OPT_x) decode_format_string("x2"); | 1255 | if (opt & OPT_x) decode_format_string("x2"); |
1296 | if (opt & OPT_s) decode_format_string("d2"); | 1256 | if (opt & OPT_s) decode_format_string("d2"); |
1297 | if (opt & OPT_S) { | 1257 | if (opt & OPT_S) { |
1298 | string_min = 3; | ||
1299 | string_min = xstrtou_sfx(str_S, 0, bkm); | 1258 | string_min = xstrtou_sfx(str_S, 0, bkm); |
1300 | flag_dump_strings = 1; | ||
1301 | } | 1259 | } |
1302 | //if (opt & OPT_w)... | ||
1303 | //if (opt & OPT_traditional)... | ||
1304 | 1260 | ||
1305 | if (flag_dump_strings && n_specs > 0) | 1261 | // Bloat: |
1306 | bb_error_msg_and_die("no type may be specified when dumping strings"); | 1262 | //if ((option_mask32 & OPT_S) && n_specs > 0) |
1263 | // bb_error_msg_and_die("no type may be specified when dumping strings"); | ||
1307 | 1264 | ||
1308 | /* If the --traditional option is used, there may be from | 1265 | /* If the --traditional option is used, there may be from |
1309 | * 0 to 3 remaining command line arguments; handle each case | 1266 | * 0 to 3 remaining command line arguments; handle each case |
1310 | * separately. | 1267 | * separately. |
1311 | * od [file] [[+]offset[.][b] [[+]label[.][b]]] | 1268 | * od [FILE] [[+]OFFSET[.][b] [[+]LABEL[.][b]]] |
1312 | * The offset and pseudo_start have the same syntax. | 1269 | * The offset and pseudo_start have the same syntax. |
1313 | * | 1270 | * |
1314 | * FIXME: POSIX 1003.1-2001 with XSI requires support for the | 1271 | * FIXME: POSIX 1003.1-2001 with XSI requires support for the |
@@ -1316,93 +1273,91 @@ int od_main(int argc, char **argv) | |||
1316 | 1273 | ||
1317 | #if ENABLE_LONG_OPTS | 1274 | #if ENABLE_LONG_OPTS |
1318 | if (opt & OPT_traditional) { | 1275 | if (opt & OPT_traditional) { |
1319 | off_t o1, o2; | 1276 | if (argv[0]) { |
1320 | 1277 | off_t pseudo_start = -1; | |
1321 | if (argc == 1) { | 1278 | off_t o1, o2; |
1322 | if (parse_old_offset(argv[0], &o1)) { | 1279 | |
1323 | n_bytes_to_skip = o1; | 1280 | if (!argv[1]) { /* one arg */ |
1324 | --argc; | 1281 | if (parse_old_offset(argv[0], &o1)) { |
1325 | ++argv; | 1282 | /* od --traditional OFFSET */ |
1326 | } | 1283 | n_bytes_to_skip = o1; |
1327 | } else if (argc == 2) { | 1284 | argv++; |
1328 | if (parse_old_offset(argv[0], &o1) | 1285 | } |
1329 | && parse_old_offset(argv[1], &o2) | 1286 | /* od --traditional FILE */ |
1330 | ) { | 1287 | } else if (!argv[2]) { /* two args */ |
1331 | n_bytes_to_skip = o1; | 1288 | if (parse_old_offset(argv[0], &o1) |
1332 | flag_pseudo_start = 1; | 1289 | && parse_old_offset(argv[1], &o2) |
1333 | pseudo_start = o2; | 1290 | ) { |
1334 | argv += 2; | 1291 | /* od --traditional OFFSET LABEL */ |
1335 | argc -= 2; | 1292 | n_bytes_to_skip = o1; |
1336 | } else if (parse_old_offset(argv[1], &o2)) { | 1293 | pseudo_start = o2; |
1337 | n_bytes_to_skip = o2; | 1294 | argv += 2; |
1338 | --argc; | 1295 | } else if (parse_old_offset(argv[1], &o2)) { |
1339 | argv[1] = argv[0]; | 1296 | /* od --traditional FILE OFFSET */ |
1340 | ++argv; | 1297 | n_bytes_to_skip = o2; |
1341 | } else { | 1298 | argv[1] = NULL; |
1342 | bb_error_msg_and_die("invalid second operand " | 1299 | } else { |
1343 | "in compatibility mode '%s'", argv[1]); | 1300 | bb_error_msg_and_die("invalid second argument '%s'", argv[1]); |
1344 | } | 1301 | } |
1345 | } else if (argc == 3) { | 1302 | } else if (!argv[3]) { /* three args */ |
1346 | if (parse_old_offset(argv[1], &o1) | 1303 | if (parse_old_offset(argv[1], &o1) |
1347 | && parse_old_offset(argv[2], &o2) | 1304 | && parse_old_offset(argv[2], &o2) |
1348 | ) { | 1305 | ) { |
1349 | n_bytes_to_skip = o1; | 1306 | /* od --traditional FILE OFFSET LABEL */ |
1350 | flag_pseudo_start = 1; | 1307 | n_bytes_to_skip = o1; |
1351 | pseudo_start = o2; | 1308 | pseudo_start = o2; |
1352 | argv[2] = argv[0]; | 1309 | argv[1] = NULL; |
1353 | argv += 2; | 1310 | } else { |
1354 | argc -= 2; | 1311 | bb_error_msg_and_die("the last two arguments must be offsets"); |
1355 | } else { | 1312 | } |
1356 | bb_error_msg_and_die("in compatibility mode " | 1313 | } else { /* >3 args */ |
1357 | "the last two arguments must be offsets"); | 1314 | bb_error_msg_and_die("too many arguments"); |
1358 | } | 1315 | } |
1359 | } else if (argc > 3) { | ||
1360 | bb_error_msg_and_die("compatibility mode supports " | ||
1361 | "at most three arguments"); | ||
1362 | } | ||
1363 | 1316 | ||
1364 | if (flag_pseudo_start) { | 1317 | if (pseudo_start >= 0) { |
1365 | if (format_address == format_address_none) { | 1318 | if (format_address == format_address_none) { |
1366 | address_base_char = 'o'; | 1319 | address_base_char = 'o'; |
1367 | address_pad_len_char = '7'; | 1320 | address_pad_len_char = '7'; |
1368 | format_address = format_address_paren; | 1321 | format_address = format_address_paren; |
1369 | } else | 1322 | } else { |
1370 | format_address = format_address_label; | 1323 | format_address = format_address_label; |
1324 | } | ||
1325 | pseudo_offset = pseudo_start - n_bytes_to_skip; | ||
1326 | } | ||
1371 | } | 1327 | } |
1328 | /* else: od --traditional (without args) */ | ||
1372 | } | 1329 | } |
1373 | #endif | 1330 | #endif |
1374 | 1331 | ||
1375 | if (limit_bytes_to_format) { | 1332 | if (option_mask32 & OPT_N) { |
1376 | end_offset = n_bytes_to_skip + max_bytes_to_format; | 1333 | end_offset = n_bytes_to_skip + max_bytes_to_format; |
1377 | if (end_offset < n_bytes_to_skip) | 1334 | if (end_offset < n_bytes_to_skip) |
1378 | bb_error_msg_and_die("skip-bytes + read-bytes is too large"); | 1335 | bb_error_msg_and_die("SKIP + SIZE is too large"); |
1379 | } | 1336 | } |
1380 | 1337 | ||
1381 | if (n_specs == 0) { | 1338 | if (n_specs == 0) { |
1382 | decode_format_string("o2"); | 1339 | decode_format_string("o2"); |
1383 | n_specs = 1; | 1340 | /*n_specs = 1; - done by decode_format_string */ |
1384 | } | 1341 | } |
1385 | 1342 | ||
1386 | /* If no files were listed on the command line, | 1343 | /* If no files were listed on the command line, |
1387 | set the global pointer FILE_LIST so that it | 1344 | set the global pointer FILE_LIST so that it |
1388 | references the null-terminated list of one name: "-". */ | 1345 | references the null-terminated list of one name: "-". */ |
1389 | file_list = bb_argv_dash; | 1346 | file_list = bb_argv_dash; |
1390 | if (argc > 0) { | 1347 | if (argv[0]) { |
1391 | /* Set the global pointer FILE_LIST so that it | 1348 | /* Set the global pointer FILE_LIST so that it |
1392 | references the first file-argument on the command-line. */ | 1349 | references the first file-argument on the command-line. */ |
1393 | file_list = (char const *const *) argv; | 1350 | file_list = (char const *const *) argv; |
1394 | } | 1351 | } |
1395 | 1352 | ||
1396 | /* open the first input file */ | 1353 | /* Open the first input file */ |
1397 | open_next_file(); | 1354 | open_next_file(); |
1398 | /* skip over any unwanted header bytes */ | 1355 | /* Skip over any unwanted header bytes */ |
1399 | skip(n_bytes_to_skip); | 1356 | skip(n_bytes_to_skip); |
1400 | if (!in_stream) | 1357 | if (!in_stream) |
1401 | return EXIT_FAILURE; | 1358 | return EXIT_FAILURE; |
1402 | 1359 | ||
1403 | pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0); | 1360 | /* Compute output block length */ |
1404 | |||
1405 | /* Compute output block length. */ | ||
1406 | l_c_m = get_lcm(); | 1361 | l_c_m = get_lcm(); |
1407 | 1362 | ||
1408 | if (opt & OPT_w) { /* -w: width */ | 1363 | if (opt & OPT_w) { /* -w: width */ |
@@ -1424,13 +1379,13 @@ int od_main(int argc, char **argv) | |||
1424 | } | 1379 | } |
1425 | #endif | 1380 | #endif |
1426 | 1381 | ||
1427 | if (flag_dump_strings) | 1382 | if (option_mask32 & OPT_S) |
1428 | dump_strings(n_bytes_to_skip, end_offset); | 1383 | dump_strings(n_bytes_to_skip, end_offset); |
1429 | else | 1384 | else |
1430 | dump(n_bytes_to_skip, end_offset); | 1385 | dump(n_bytes_to_skip, end_offset); |
1431 | 1386 | ||
1432 | if (fclose(stdin) == EOF) | 1387 | if (fclose(stdin)) |
1433 | bb_perror_msg_and_die(bb_msg_standard_input); | 1388 | bb_perror_msg_and_die(bb_msg_standard_input); |
1434 | 1389 | ||
1435 | return ioerror; | 1390 | return exit_code; |
1436 | } | 1391 | } |