diff options
author | Ron Yorston <rmy@pobox.com> | 2022-10-12 10:43:26 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2022-10-12 10:43:26 +0100 |
commit | 49c6f079acb4edae84b4496bd941cdbb5048ba01 (patch) | |
tree | ac54ecaad45050f7bfe274a11db29882aa32a9a8 /util-linux | |
parent | a55cf07365ec2ff51749a77e09ae9edac79a99fe (diff) | |
parent | c8c1fcdba163f264a503380bc63485aacd09214c (diff) | |
download | busybox-w32-49c6f079acb4edae84b4496bd941cdbb5048ba01.tar.gz busybox-w32-49c6f079acb4edae84b4496bd941cdbb5048ba01.tar.bz2 busybox-w32-49c6f079acb4edae84b4496bd941cdbb5048ba01.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'util-linux')
-rw-r--r-- | util-linux/hexdump_xxd.c | 114 |
1 files changed, 89 insertions, 25 deletions
diff --git a/util-linux/hexdump_xxd.c b/util-linux/hexdump_xxd.c index 4372ac770..6629407de 100644 --- a/util-linux/hexdump_xxd.c +++ b/util-linux/hexdump_xxd.c | |||
@@ -55,6 +55,7 @@ | |||
55 | //usage: "\n -r Reverse (with -p, assumes no offsets in input)" | 55 | //usage: "\n -r Reverse (with -p, assumes no offsets in input)" |
56 | 56 | ||
57 | #include "libbb.h" | 57 | #include "libbb.h" |
58 | #include "common_bufsiz.h" | ||
58 | #include "dump.h" | 59 | #include "dump.h" |
59 | 60 | ||
60 | /* This is a NOEXEC applet. Be very careful! */ | 61 | /* This is a NOEXEC applet. Be very careful! */ |
@@ -69,61 +70,110 @@ | |||
69 | #define OPT_c (1 << 7) | 70 | #define OPT_c (1 << 7) |
70 | #define OPT_o (1 << 8) | 71 | #define OPT_o (1 << 8) |
71 | 72 | ||
72 | static void reverse(unsigned opt, const char *filename) | 73 | #define fillbuf bb_common_bufsiz1 |
74 | |||
75 | static void write_zeros(off_t count) | ||
76 | { | ||
77 | errno = 0; | ||
78 | do { | ||
79 | unsigned sz = count < COMMON_BUFSIZE ? (unsigned)count : COMMON_BUFSIZE; | ||
80 | if (fwrite(fillbuf, 1, sz, stdout) != sz) | ||
81 | bb_perror_msg_and_die("write error"); | ||
82 | count -= sz; | ||
83 | } while (count != 0); | ||
84 | } | ||
85 | |||
86 | static void reverse(unsigned opt, const char *filename, char *opt_s) | ||
73 | { | 87 | { |
74 | FILE *fp; | 88 | FILE *fp; |
75 | char *buf; | 89 | char *buf; |
90 | off_t cur, opt_s_ofs; | ||
91 | |||
92 | memset(fillbuf, 0, COMMON_BUFSIZE); | ||
93 | opt_s_ofs = cur = 0; | ||
94 | if (opt_s) { | ||
95 | opt_s_ofs = BB_STRTOOFF(opt_s, NULL, 0); | ||
96 | if (errno || opt_s_ofs < 0) | ||
97 | bb_error_msg_and_die("invalid number '%s'", opt_s); | ||
98 | } | ||
76 | 99 | ||
77 | fp = filename ? xfopen_for_read(filename) : stdin; | 100 | fp = filename ? xfopen_for_read(filename) : stdin; |
78 | 101 | ||
102 | get_new_line: | ||
79 | while ((buf = xmalloc_fgetline(fp)) != NULL) { | 103 | while ((buf = xmalloc_fgetline(fp)) != NULL) { |
80 | char *p; | 104 | char *p; |
81 | 105 | ||
82 | p = buf; | 106 | p = buf; |
83 | if (!(opt & OPT_p)) { | 107 | if (!(opt & OPT_p)) { |
84 | /* skip address */ | 108 | char *end; |
85 | while (isxdigit(*p)) p++; | 109 | off_t ofs; |
110 | skip_address: | ||
111 | p = skip_whitespace(p); | ||
112 | ofs = BB_STRTOOFF(p, &end, 16); | ||
113 | if ((errno && errno != EINVAL) | ||
114 | || ofs < 0 | ||
115 | /* -s SEEK value should be added before seeking */ | ||
116 | || (ofs += opt_s_ofs) < 0 | ||
117 | ) { | ||
118 | bb_error_msg_and_die("invalid number '%s'", p); | ||
119 | } | ||
120 | if (ofs != cur) { | ||
121 | if (fseeko(stdout, ofs, SEEK_SET) != 0) { | ||
122 | if (ofs < cur) | ||
123 | bb_perror_msg_and_die("cannot seek"); | ||
124 | write_zeros(ofs - cur); | ||
125 | } | ||
126 | cur = ofs; | ||
127 | } | ||
128 | p = end; | ||
86 | /* NB: for xxd -r, first hex portion is address even without colon */ | 129 | /* NB: for xxd -r, first hex portion is address even without colon */ |
87 | /* If it's there, skip it: */ | 130 | /* But if colon is there, skip it: */ |
88 | if (*p == ':') p++; | 131 | if (*p == ':') |
89 | 132 | p++; | |
90 | //TODO: seek (or zero-pad if unseekable) to the address position | ||
91 | //NOTE: -s SEEK value should be added to the address before seeking | ||
92 | } | 133 | } |
93 | 134 | ||
94 | /* Process hex bytes optionally separated by whitespace */ | 135 | /* Process hex bytes optionally separated by whitespace */ |
95 | for (;;) { | 136 | for (;;) { |
96 | uint8_t val, c; | 137 | uint8_t val, c; |
138 | int badchar = 0; | ||
97 | nibble1: | 139 | nibble1: |
98 | p = skip_whitespace(p); | 140 | if (opt & OPT_p) |
99 | 141 | p = skip_whitespace(p); | |
100 | c = *p++; | 142 | c = *p++; |
101 | if (isdigit(c)) | 143 | if (isdigit(c)) |
102 | val = c - '0'; | 144 | val = c - '0'; |
103 | else if ((c|0x20) >= 'a' && (c|0x20) <= 'f') | 145 | else if ((c|0x20) >= 'a' && (c|0x20) <= 'f') |
104 | val = (c|0x20) - ('a' - 10); | 146 | val = (c|0x20) - ('a' - 10); |
105 | else { | 147 | else { |
106 | /* xxd V1.10 is inconsistent here. | 148 | /* xxd V1.10 allows one non-hexnum char: |
107 | * echo -e "31 !3 0a 0a" | xxd -r -p | 149 | * echo -e "31 !3 0a 0a" | xxd -r -p |
108 | * is "10<a0>" (no <cr>) - "!" is ignored, | 150 | * is "10<a0>" (no <cr>) - "!" is ignored, |
109 | * but | 151 | * but stops for more than one: |
110 | * echo -e "31 !!343434\n30 0a" | xxd -r -p | 152 | * echo -e "31 !!343434\n30 0a" | xxd -r -p |
111 | * is "10<cr>" - "!!" drops rest of the line. | 153 | * is "10<cr>" - "!!" drops rest of the line. |
112 | * We will ignore all invalid chars: | 154 | * Note: this also covers whitespace chars: |
155 | * xxxxxxxx: 3031 3233 3435 3637 3839 3a3b 3c3d 3e3f 0123456789:;<=>? | ||
156 | * detects this ^ - skips this one space | ||
157 | * xxxxxxxx: 3031 3233 3435 3637 3839 3a3b 3c3d 3e3f 0123456789:;<=>? | ||
158 | * detects this ^^ - skips the rest | ||
113 | */ | 159 | */ |
114 | if (c != '\0') | 160 | if (c == '\0' || badchar) |
115 | goto nibble1; | 161 | break; |
116 | break; | 162 | badchar++; |
163 | goto nibble1; | ||
117 | } | 164 | } |
118 | val <<= 4; | 165 | val <<= 4; |
119 | 166 | ||
120 | /* Works the same with xxd V1.10: | ||
121 | * echo "31 09 32 0a" | xxd -r -p | ||
122 | * echo "31 0 9 32 0a" | xxd -r -p | ||
123 | * thus allow whitespace even within the byte: | ||
124 | */ | ||
125 | nibble2: | 167 | nibble2: |
126 | p = skip_whitespace(p); | 168 | if (opt & OPT_p) { |
169 | /* Works the same with xxd V1.10: | ||
170 | * echo "31 09 32 0a" | xxd -r -p | ||
171 | * echo "31 0 9 32 0a" | xxd -r -p | ||
172 | * thus allow whitespace (even multiple chars) | ||
173 | * after byte's 1st char: | ||
174 | */ | ||
175 | p = skip_whitespace(p); | ||
176 | } | ||
127 | 177 | ||
128 | c = *p++; | 178 | c = *p++; |
129 | if (isdigit(c)) | 179 | if (isdigit(c)) |
@@ -132,7 +182,16 @@ static void reverse(unsigned opt, const char *filename) | |||
132 | val |= (c|0x20) - ('a' - 10); | 182 | val |= (c|0x20) - ('a' - 10); |
133 | else { | 183 | else { |
134 | if (c != '\0') { | 184 | if (c != '\0') { |
135 | /* "...3<not_hex_char>..." ignores both chars */ | 185 | /* "...3<not_hex_char>...": ignore "3", |
186 | * skip everything up to next hexchar or newline: | ||
187 | */ | ||
188 | while (!isxdigit(*p)) { | ||
189 | if (*p == '\0') { | ||
190 | free(buf); | ||
191 | goto get_new_line; | ||
192 | } | ||
193 | p++; | ||
194 | } | ||
136 | goto nibble1; | 195 | goto nibble1; |
137 | } | 196 | } |
138 | /* Nibbles can join even through newline: | 197 | /* Nibbles can join even through newline: |
@@ -143,10 +202,13 @@ static void reverse(unsigned opt, const char *filename) | |||
143 | p = buf = xmalloc_fgetline(fp); | 202 | p = buf = xmalloc_fgetline(fp); |
144 | if (!buf) | 203 | if (!buf) |
145 | break; | 204 | break; |
205 | if (!(opt & OPT_p)) /* -p and !-p: different behavior */ | ||
206 | goto skip_address; | ||
146 | goto nibble2; | 207 | goto nibble2; |
147 | } | 208 | } |
148 | putchar(val); | 209 | putchar(val); |
149 | } | 210 | cur++; |
211 | } /* for(;;) */ | ||
150 | free(buf); | 212 | free(buf); |
151 | } | 213 | } |
152 | //fclose(fp); | 214 | //fclose(fp); |
@@ -173,6 +235,8 @@ int xxd_main(int argc UNUSED_PARAM, char **argv) | |||
173 | unsigned opt; | 235 | unsigned opt; |
174 | int r; | 236 | int r; |
175 | 237 | ||
238 | setup_common_bufsiz(); | ||
239 | |||
176 | dumper = alloc_dumper(); | 240 | dumper = alloc_dumper(); |
177 | 241 | ||
178 | opt = getopt32(argv, "^" "l:s:apirg:+c:+o:" "\0" "?1" /* 1 argument max */, | 242 | opt = getopt32(argv, "^" "l:s:apirg:+c:+o:" "\0" "?1" /* 1 argument max */, |
@@ -200,7 +264,7 @@ int xxd_main(int argc UNUSED_PARAM, char **argv) | |||
200 | } | 264 | } |
201 | 265 | ||
202 | if (opt & OPT_r) { | 266 | if (opt & OPT_r) { |
203 | reverse(opt, argv[0]); | 267 | reverse(opt, argv[0], opt_s); |
204 | } | 268 | } |
205 | 269 | ||
206 | if (opt & OPT_o) { | 270 | if (opt & OPT_o) { |