Skip to content

Commit 6fbb7bb

Browse files
authored
hexdump: add more formats (#850)
* hexdump: add more output formats * Default output format switches to two-byte hex to match other versions, with canonical hex+ascii as -C option * Add -b, -d, -o and -x format options as supported on OpenBSD * Switch default output to filter duplicate lines, with -v option to disable this filtering * Sync usage string and pod manual * stray comment
1 parent c5da68a commit 6fbb7bb

File tree

1 file changed

+166
-19
lines changed

1 file changed

+166
-19
lines changed

bin/hexdump

+166-19
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ License: artistic2
1212
=cut
1313

1414
use strict;
15-
use warnings;
1615

1716
use File::Basename qw(basename);
1817
use Getopt::Std qw(getopts);
@@ -23,7 +22,7 @@ use constant EX_FAILURE => 1;
2322
my $Program = basename($0);
2423
my $VERSION = '0.1';
2524

26-
my (%opt, @chars, @cesc, $adr, $dump, $fmt, $nread, $curf, $skip);
25+
my (%opt, @chars, @cesc, $adr, $dump, $fmt, $nread, $curf, $skip, $prev, $dupl);
2726

2827
sub VERSION_MESSAGE {
2928
print "$Program version $VERSION\n";
@@ -58,6 +57,18 @@ sub doskip {
5857
}
5958

6059
sub dump_c {
60+
unless ($opt{'v'}) {
61+
my $str = join '', @chars;
62+
if ($str eq $prev) {
63+
print "*\n" unless $dupl;
64+
$dupl = 1;
65+
$adr += scalar @chars;
66+
undef @chars;
67+
return;
68+
}
69+
$dupl = 0;
70+
$prev = $str;
71+
}
6172
printf "$fmt ", $adr;
6273
foreach my $c (@chars) {
6374
if ($c =~ m/[[:print:]]/) {
@@ -74,18 +85,121 @@ sub dump_c {
7485
undef @chars;
7586
}
7687

77-
sub dump_x {
78-
printf "$fmt ", $adr;
88+
sub dump_hex1 {
7989
my $str = join '', @chars;
90+
unless ($opt{'v'}) {
91+
if ($str eq $prev) {
92+
print "*\n" unless $dupl;
93+
$dupl = 1;
94+
$adr += scalar @chars;
95+
undef @chars;
96+
return;
97+
}
98+
$dupl = 0;
99+
$prev = $str;
100+
}
80101
my $hex = unpack 'H*', $str;
81102
$hex =~ s/^(.{16})/$1 /;
82103
$hex =~ s/(\S{2})/ $1/g;
83104
$str =~ s/[^[:print:]]/./g;
105+
printf "$fmt ", $adr;
84106
printf "%-51s|%s|\n", $hex, $str;
85107
$adr += scalar @chars;
86108
undef @chars;
87109
}
88110

111+
sub dump_hex2 {
112+
my $str = join '', @chars;
113+
unless ($opt{'v'}) {
114+
if ($str eq $prev) {
115+
print "*\n" unless $dupl;
116+
$dupl = 1;
117+
$adr += scalar @chars;
118+
undef @chars;
119+
return;
120+
}
121+
$dupl = 0;
122+
$prev = $str;
123+
}
124+
printf "$fmt ", $adr;
125+
my @words = unpack 'S*', $str;
126+
foreach my $i (@words) {
127+
printf ' %04x ', $i;
128+
}
129+
print "\n";
130+
$adr += scalar @chars;
131+
undef @chars;
132+
}
133+
134+
sub dump_dec2 {
135+
my $str = join '', @chars;
136+
unless ($opt{'v'}) {
137+
if ($str eq $prev) {
138+
print "*\n" unless $dupl;
139+
$dupl = 1;
140+
$adr += scalar @chars;
141+
undef @chars;
142+
return;
143+
}
144+
$dupl = 0;
145+
$prev = $str;
146+
}
147+
printf "$fmt ", $adr;
148+
my @words = unpack 'S*', $str;
149+
foreach my $i (@words) {
150+
printf ' %05d ', $i;
151+
}
152+
print "\n";
153+
$adr += scalar @chars;
154+
undef @chars;
155+
}
156+
157+
sub dump_oct1 {
158+
unless ($opt{'v'}) {
159+
my $str = join '', @chars;
160+
if ($str eq $prev) {
161+
print "*\n" unless $dupl;
162+
$dupl = 1;
163+
$adr += scalar @chars;
164+
undef @chars;
165+
return;
166+
}
167+
$dupl = 0;
168+
$prev = $str;
169+
}
170+
printf "$fmt ", $adr;
171+
foreach my $c (@chars) {
172+
my $i = ord $c;
173+
printf '%03o ', $i;
174+
}
175+
print "\n";
176+
$adr += scalar @chars;
177+
undef @chars;
178+
}
179+
180+
sub dump_oct2 {
181+
my $str = join '', @chars;
182+
unless ($opt{'v'}) {
183+
if ($str eq $prev) {
184+
print "*\n" unless $dupl;
185+
$dupl = 1;
186+
$adr += scalar @chars;
187+
undef @chars;
188+
return;
189+
}
190+
$dupl = 0;
191+
$prev = $str;
192+
}
193+
printf "$fmt ", $adr;
194+
my @words = unpack 'S*', $str;
195+
foreach my $i (@words) {
196+
printf ' %06o ', $i;
197+
}
198+
print "\n";
199+
$adr += scalar @chars;
200+
undef @chars;
201+
}
202+
89203
sub dofile {
90204
doskip() if $skip;
91205
my $c;
@@ -128,8 +242,16 @@ sub revert {
128242
}
129243

130244
sub xd {
245+
$prev = '';
131246
$adr = $nread = 0;
132-
if ($opt{'c'}) {
247+
$fmt = '%08lx';
248+
$dump = \&dump_hex2;
249+
250+
if ($opt{'b'}) {
251+
$dump = \&dump_oct1;
252+
} elsif ($opt{'C'}) {
253+
$dump = \&dump_hex1;
254+
} elsif ($opt{'c'}) {
133255
$fmt = '%07lx';
134256
$dump = \&dump_c;
135257
$cesc[0] = ' \0 ';
@@ -140,9 +262,10 @@ sub xd {
140262
$cesc[11] = ' \v ';
141263
$cesc[12] = ' \f ';
142264
$cesc[13] = ' \r ';
143-
} else {
144-
$fmt = '%08lx';
145-
$dump = \&dump_x;
265+
} elsif ($opt{'d'}) {
266+
$dump = \&dump_dec2;
267+
} elsif ($opt{'o'}) {
268+
$dump = \&dump_oct2;
146269
}
147270
if (@ARGV) {
148271
while (@ARGV) {
@@ -161,9 +284,9 @@ sub xd {
161284
printf "$fmt\n", $adr;
162285
}
163286

164-
getopts('cn:rs:', \%opt)
287+
getopts('bCcdn:ors:vx', \%opt)
165288
or do {
166-
warn "usage: $Program [-cr] [-n length] [-s skip] [file ...]\n";
289+
warn "usage: $Program [-bCcdorvx] [-n length] [-s skip] [file ...]\n";
167290
exit EX_FAILURE;
168291
};
169292

@@ -187,34 +310,52 @@ hexdump - print input as hexadecimal
187310
188311
=head1 SYNOPSIS
189312
190-
hexdump [-c] [-n NUMBER] [-s NUMBER] [file ...]
313+
hexdump [-bCcdovx] [-n NUMBER] [-s NUMBER] [file ...]
191314
192315
hexdump [-r] [file ...]
193316
194317
=head1 DESCRIPTION
195318
196-
Data is read from standard input if no file arguments are provided. The
197-
default output mode is canonical hex+ASCII. Each line begins with an offset
198-
number followed by a space-separated list of 16 hex bytes. Finally, printable
199-
input characters are listed between two '|' characters.
319+
Input files are taken as a single stream of data and formatted as hexadecimal.
320+
Standard input is used if no file arguments are provided.
321+
Duplicate lines of input are filtered by default.
322+
A '*' character is printed to indicate one or more duplicate input lines.
200323
201324
=head2 OPTIONS
202325
203326
The following options are available:
204327
205328
=over 4
206329
330+
=item -b
331+
332+
One-byte octal output.
333+
334+
=item -C
335+
336+
Output canonical hex+ASCII. Each line begins with an offset number followed
337+
by a space-separated list of 16 hex bytes.
338+
Printable input characters are listed between two '|' characters.
339+
207340
=item -c
208341
209342
Output a space-separated list of ASCII characters. Non-print characters
210343
are listed in octal, or a C-escape code.
211344
345+
=item -d
346+
347+
Format output as two-byte decimal.
348+
212349
=item -n NUMBER
213350
214351
Terminate the process after reading a set NUMBER of input bytes.
215352
The number argument must be given in decimal. Input skipped by the -s
216353
option is not counted.
217354
355+
=item -o
356+
357+
Format output as two-byte octal.
358+
218359
=item -r
219360
220361
Revert a hex dump back to binary. Input is expected to be formatted as
@@ -223,24 +364,30 @@ each line are ignored. Input lines may have zero or more hex bytes;
223364
running over 16 bytes is supported. Spaces between hex digits are ignored.
224365
An odd number of hex digits on a line results in an error.
225366
226-
Setting -r causes all other options to be ignored except -u. It is
227-
possible to specify multiple input files.
367+
Setting -r causes all other options.
368+
It is possible to specify multiple input files.
228369
229370
=item -s NUMBER
230371
231372
Skip a set NUMBER of bytes at the beginning of input. The number argument
232373
must be given in decimal. The offset number printed on output is advanced
233374
to reflect the skipped bytes.
234375
376+
=item -v
377+
378+
Duplicate lines of output are displayed.
379+
380+
=item -x
381+
382+
Format output as two-byte hexadecimal. This is the default.
383+
235384
=back
236385
237386
=head1 BUGS
238387
239388
No option exists for setting an output filename, so the -r option will
240389
write binary data to a terminal if output is not redirected.
241390
242-
No support for multi-byte hex display, or plain hex output.
243-
244391
=head1 AUTHOR
245392
246393
Written by Michael Mikonos.

0 commit comments

Comments
 (0)