File Coverage

bin/od
Criterion Covered Total %
statement 100 221 45.2
branch 44 130 33.8
condition 11 24 45.8
subroutine 17 38 44.7
pod n/a
total 172 413 41.6


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             =begin metadata
4              
5             Name: od
6             Description: dump files in octal and other formats
7             Author: Mark Kahn, mkahn@vbe.com
8             Author: Michael Mikonos
9             License: perl
10              
11             =end metadata
12              
13             =cut
14              
15              
16 6     6   27963 use strict;
  6         10  
  6         280  
17              
18 6     6   25 use File::Basename qw(basename);
  6         10  
  6         571  
19 6     6   2700 use Getopt::Std qw(getopts);
  6         13888  
  6         419  
20              
21 6     6   37 use constant EX_SUCCESS => 0;
  6         16  
  6         618  
22 6     6   26 use constant EX_FAILURE => 1;
  6         10  
  6         281  
23 6     6   27 use constant LINESZ => 16;
  6         6  
  6         211  
24 6     6   22 use constant PRINTMAX => 126;
  6         8  
  6         393  
25              
26 6         25947 use vars qw/ $opt_A $opt_a $opt_B $opt_b $opt_c $opt_D $opt_d $opt_e $opt_F
27             $opt_f $opt_H $opt_h $opt_i $opt_j $opt_l $opt_N $opt_O $opt_o $opt_s
28 6     6   38 $opt_t $opt_v $opt_X $opt_x /;
  6         12  
29              
30 6         909925 our $VERSION = '1.4';
31              
32 6         35 my ($offset1, $radix, $data, @arr, $lim);
33 6         0 my ($lastline, $strfmt, $ml);
34              
35 6         96 my %charescs = (
36             0 => ' \0',
37             7 => ' \a',
38             8 => ' \b',
39             9 => ' \t',
40             10 => ' \n',
41             11 => ' \v',
42             12 => ' \f',
43             13 => ' \r',
44             92 => ' \\\\',
45             );
46              
47             # embedded space allows formatting without sprintf
48 6         191 my %charname = (
49             0 => 'nul',
50             1 => 'soh',
51             2 => 'stx',
52             3 => 'etx',
53             4 => 'eot',
54             5 => 'enq',
55             6 => 'ack',
56             7 => 'bel',
57             8 => ' bs',
58             9 => ' ht',
59             10 => ' nl',
60             11 => ' vt',
61             12 => ' ff',
62             13 => ' cr',
63             14 => ' so',
64             15 => ' si',
65             16 => 'dle',
66             17 => 'dc1',
67             18 => 'dc2',
68             19 => 'dc3',
69             20 => 'dc4',
70             21 => 'nak',
71             22 => 'syn',
72             23 => 'etb',
73             24 => 'can',
74             25 => ' em',
75             26 => 'sub',
76             27 => 'esc',
77             28 => ' fs',
78             29 => ' gs',
79             30 => ' rs',
80             31 => ' us',
81             32 => ' sp',
82             127 => 'del',
83             );
84              
85 6         13 $offset1 = 0;
86 6         9 $lastline = '';
87              
88 6         359 my $Program = basename($0);
89              
90 6 50       53 getopts('A:aBbcDdeFfHhij:lN:Oost:vXx') or help();
91 6 50       817 if (defined $opt_A) {
92 0 0       0 if ($opt_A !~ m/\A[doxn]\z/) {
93 0         0 warn "$Program: unexpected radix: '$opt_A'\n";
94 0         0 exit EX_FAILURE;
95             }
96 0 0       0 if ($opt_A ne 'n') {
97 0         0 $radix = $opt_A;
98             }
99             }
100             else {
101 6         13 $radix = 'o';
102             }
103              
104 6 100       50 if (defined $opt_j) {
105 1 50       6 if ($opt_j =~ m/\D/) {
106 0         0 warn "$Program: bad argument to -j: '$opt_j'\n";
107 0         0 exit EX_FAILURE;
108             }
109             }
110 6 100       39 if (defined $opt_N) {
111 1 50       4 if ($opt_N =~ m/\D/) {
112 0         0 warn "$Program: bad argument to -N: '$opt_N'\n";
113 0         0 exit EX_FAILURE;
114             }
115 1         1 $lim = $opt_N;
116             }
117              
118 6         22 my $fmt;
119 6 50 33     166 if ($opt_a) {
    50 33        
    50 33        
    50 33        
    50 33        
    50          
    50          
    50          
    100          
    50          
    50          
    50          
    50          
120 0         0 $fmt = \&char7bit;
121             }
122             elsif ($opt_b) {
123 0         0 $fmt = \&octal1;
124             }
125             elsif ($opt_c) {
126 0         0 $fmt = \&char1;
127             }
128             elsif ($opt_D) {
129 0         0 $fmt = \&udecimal4;
130             }
131             elsif ($opt_d) {
132 0         0 $fmt = \&udecimal2;
133             }
134             elsif ($opt_e || $opt_F) {
135 0         0 $fmt = \&float8;
136             }
137             elsif ($opt_f) {
138 0         0 $fmt = \&float4;
139             }
140             elsif ($opt_H || $opt_X) {
141 0         0 $fmt = \&hex4;
142             }
143             elsif ($opt_h || $opt_x) {
144 3         7 $fmt = \&hex2;
145             }
146             elsif ($opt_i || $opt_s) {
147 0         0 $fmt = \&decimal2;
148             }
149             elsif ($opt_l) {
150 0         0 $fmt = \&long;
151             }
152             elsif ($opt_O) {
153 0         0 $fmt = \&octal4;
154             }
155             elsif ($opt_B || $opt_o) {
156 0         0 $fmt = \&octal2;
157             }
158             else {
159 3         8 $fmt = \&octal2;
160             }
161              
162 6 50       19 if (defined $opt_t) {
163 0 0       0 if ($opt_t eq 'x1') {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
164 0         0 $fmt = \&hex1;
165             } elsif ($opt_t eq 'x2') {
166 0         0 $fmt = \&hex2;
167             } elsif ($opt_t eq 'x4') {
168 0         0 $fmt = \&hex4;
169             } elsif ($opt_t eq 'x8') {
170 0         0 $fmt = \&hex8;
171             } elsif ($opt_t eq 'o1') {
172 0         0 $fmt = \&octal1;
173             } elsif ($opt_t eq 'o2') {
174 0         0 $fmt = \&octal2;
175             } elsif ($opt_t eq 'o4') {
176 0         0 $fmt = \&octal4;
177             } elsif ($opt_t eq 'o8') {
178 0         0 $fmt = \&octal8;
179             } elsif ($opt_t eq 'd1') {
180 0         0 $fmt = \&decimal1;
181             } elsif ($opt_t eq 'd2') {
182 0         0 $fmt = \&decimal2;
183             } elsif ($opt_t eq 'd4') {
184 0         0 $fmt = \&decimal4;
185             } elsif ($opt_t eq 'd8') {
186 0         0 $fmt = \&decimal8;
187             } elsif ($opt_t eq 'u1') {
188 0         0 $fmt = \&udecimal1;
189             } elsif ($opt_t eq 'u2') {
190 0         0 $fmt = \&udecimal2;
191             } elsif ($opt_t eq 'u4') {
192 0         0 $fmt = \&udecimal4;
193             } elsif ($opt_t eq 'u8') {
194 0         0 $fmt = \&udecimal8;
195             } elsif ($opt_t eq 'f4') {
196 0         0 $fmt = \&float4;
197             } elsif ($opt_t eq 'f8') {
198 0         0 $fmt = \&float8;
199             } elsif ($opt_t eq 'a') {
200 0         0 $fmt = \&char7bit;
201             } elsif ($opt_t eq 'c') {
202 0         0 $fmt = \&char1;
203             } else {
204 0         0 warn "$Program: unexpected output format specifier\n";
205 0         0 exit EX_FAILURE;
206             }
207 0 0       0 if ($opt_t =~ m/\A[doux]8\Z/) {
208 0         0 my $has_quad = eval {
209 0         0 unpack 'Q', '';
210 0         0 1;
211             };
212 0 0       0 unless ($has_quad) {
213 0         0 warn "$Program: 64-bit perl needed for $opt_t format\n";
214 0         0 exit EX_FAILURE;
215             }
216             }
217             }
218              
219 6         17 my $nread = 0;
220 6         10 my $rc = EX_SUCCESS;
221 6         21 foreach my $file (@ARGV) {
222 5 50       217 if (-d $file) {
223 0         0 warn "$Program: '$file' is a directory\n";
224 0         0 $rc = EX_FAILURE;
225 0         0 next;
226             }
227 5         10 my $fh;
228 5 50       236 unless (open $fh, '<', $file) {
229 0         0 warn "$Program: cannot open '$file': $!\n";
230 0         0 $rc = EX_FAILURE;
231 0         0 next;
232             }
233 5         19 binmode $fh;
234              
235 5 100       61 do_skip($fh) if $opt_j;
236 5         51 dump_file($fh);
237 5         74 close $fh;
238             }
239 6 100       24 unless (@ARGV) {
240 1 50       5 do_skip(*STDIN) if $opt_j;
241 1         5 dump_file(*STDIN);
242             }
243 6 50       16 dump_line() if (defined $data);
244 6         16 emit_offset(1);
245 6         0 exit $rc;
246              
247             sub VERSION_MESSAGE {
248 0     0   0 print "$Program version $VERSION\n";
249 0         0 exit EX_SUCCESS;
250             }
251              
252             sub limit_reached {
253 1030   100 1030   2615 return defined($lim) && $nread >= $lim;
254             }
255              
256             sub emit_offset {
257 70     70   75 my $nl = shift;
258 70 50       103 return unless $radix;
259 70 100       212 printf "%.8$radix%s", $offset1, $nl ? "\n" : ' ';
260             }
261              
262             sub dump_line {
263 64 50   64   81 unless ($opt_v) {
264 64 50       84 if (diffdata()) {
265 64         56 $lastline = $data . '|';
266 64         70 $ml = 0;
267             } else {
268 0 0       0 print "*\n" unless $ml;
269 0         0 $ml = 1;
270             }
271             }
272 64 50       87 unless ($ml) {
273 64         87 emit_offset();
274 64         92 &$fmt;
275 64         181 printf "$strfmt\n", @arr;
276             }
277 64         87 $offset1 += length $data;
278 64         86 undef $data;
279             }
280              
281             sub dump_file {
282 6     6   43 my $fh = shift;
283 6         9 my $buf;
284              
285 6   100     37 while (!limit_reached() && !eof($fh)) {
286 1024         1106 my $len = read $fh, $buf, 1;
287 1024 50       1124 unless (defined $len) {
288 0         0 warn "$Program: read error: $!\n";
289 0         0 $rc = EX_FAILURE;
290 0         0 return;
291             }
292 1024         799 $data .= $buf;
293 1024         722 $nread++;
294              
295 1024 100       1317 dump_line() if (length($data) == LINESZ);
296             }
297             }
298              
299             sub do_skip {
300 1     1   2 my $fh = shift;
301 1         2 my $buf;
302              
303 1         4 while ($opt_j > 0) {
304 16         53 my $len = read $fh, $buf, 1;
305 16 50       36 if ($len == 0) {
306 0         0 warn "$Program: skip past end of input\n";
307 0         0 exit EX_FAILURE;
308             }
309 16 50       30 unless (defined $len) {
310 0         0 warn "$Program: read error: $!\n";
311 0         0 exit EX_FAILURE;
312             }
313 16         37 $opt_j--;
314 16         34 $offset1++;
315             }
316             }
317              
318             sub octal1 {
319 0     0   0 @arr = unpack 'C*', $data;
320 0         0 $strfmt = '%.3o ' x (scalar @arr);
321             }
322              
323             sub decimal1 {
324 0     0   0 @arr = unpack 'c*', $data;
325 0         0 $strfmt = '%4d ' x (scalar @arr);
326             }
327              
328             sub udecimal1 {
329 0     0   0 @arr = unpack 'C*', $data;
330 0         0 $strfmt = '%3u ' x (scalar @arr);
331             }
332              
333             sub hex1 {
334 0     0   0 @arr = unpack 'C*', $data;
335 0         0 $strfmt = '%.2x ' x (scalar @arr);
336             }
337              
338             sub char1 {
339 0     0   0 @arr = ();
340 0         0 my @arr1 = unpack 'C*', $data;
341 0         0 for my $val (@arr1) {
342 0 0 0     0 if (exists $charescs{$val}) {
    0          
343 0         0 $arr[0] .= $charescs{$val} . " ";
344             }
345             elsif ($val > PRINTMAX || chr($val) !~ m/[[:print:]]/) {
346 0         0 $arr[0] .= sprintf('%03o ', $val);
347             }
348             else {
349 0         0 $arr[0] .= " " . chr($val) . " ";
350             }
351             }
352 0         0 $strfmt = '%s';
353             }
354              
355             sub char7bit {
356 0     0   0 @arr = ();
357 0         0 my @arr1 = unpack 'C*', $data;
358 0         0 for my $val (@arr1) {
359 0         0 my $n = $val & 0x7f;
360 0 0       0 if (exists $charname{$n}) {
361 0         0 $arr[0] .= $charname{$n} . " ";
362             }
363             else {
364 0         0 $arr[0] .= " " . chr($n) . " ";
365             }
366             }
367 0         0 $strfmt = '%s';
368             }
369              
370             sub udecimal2 {
371 0     0   0 @arr = unpack 'S*', $data . zeropad(length($data), 2);
372 0         0 $strfmt = '%5u ' x (scalar @arr);
373             }
374              
375             sub float4 {
376 0     0   0 @arr = unpack 'f*', $data . zeropad(length($data), 4);
377 0         0 $strfmt = '%15.7e ' x (scalar @arr);
378             }
379              
380             sub float8 {
381 0     0   0 @arr = unpack 'd*', $data . zeropad(length($data), 8);
382 0         0 $strfmt = '%24.16e ' x (scalar @arr);
383             }
384              
385             sub decimal2 {
386 0     0   0 @arr = unpack 's*', $data . zeropad(length($data), 2);
387 0         0 $strfmt = '%6d ' x (scalar @arr);
388             }
389              
390             sub long {
391 0     0   0 @arr = unpack 'L*', $data . zeropad(length($data), 4);
392 0         0 $strfmt = '%10ld ' x (scalar @arr);
393             }
394              
395             sub octal2 {
396 32     32   28 @arr = unpack 'S*', $data . zeropad(length($data), 2);
397 32         37 $strfmt = '%.6o ' x (scalar @arr);
398             }
399              
400             sub octal4 {
401 0     0   0 @arr = unpack 'L*', $data . zeropad(length($data), 4);
402 0         0 $strfmt = '%.11o ' x (scalar @arr);
403             }
404              
405             sub decimal4 {
406 0     0   0 @arr = unpack 'L*', $data . zeropad(length($data), 4);
407 0         0 $strfmt = '%11d ' x (scalar @arr);
408             }
409              
410             sub udecimal4 {
411 0     0   0 @arr = unpack 'L*', $data . zeropad(length($data), 4);
412 0         0 $strfmt = '%11u ' x (scalar @arr);
413             }
414              
415             sub hex2 {
416 32     32   54 @arr = unpack 'S*', $data . zeropad(length($data), 2);
417 32         59 $strfmt = '%.4x ' x (scalar @arr);
418             }
419              
420             sub hex4 {
421 0     0   0 @arr = unpack 'L*', $data . zeropad(length($data), 4);
422 0         0 $strfmt = '%.8x ' x (scalar @arr);
423             }
424              
425             sub hex8 {
426 0     0   0 @arr = unpack 'Q*', $data . zeropad(length($data), 8);
427 0         0 $strfmt = '%.16x ' x (scalar @arr);
428             }
429              
430             sub octal8 {
431 0     0   0 @arr = unpack 'Q*', $data . zeropad(length($data), 8);
432 0         0 $strfmt = '%.22o ' x (scalar @arr);
433             }
434              
435             sub udecimal8 {
436 0     0   0 @arr = unpack 'Q*', $data . zeropad(length($data), 8);
437 0         0 $strfmt = '%22u ' x (scalar @arr);
438             }
439              
440             sub decimal8 {
441 0     0   0 @arr = unpack 'Q*', $data . zeropad(length($data), 8);
442 0         0 $strfmt = '%22d ' x (scalar @arr);
443             }
444              
445             sub zeropad {
446 64     64   89 my ($len, $wantbytes) = @_;
447 64         85 my $remain = $len % $wantbytes;
448 64 50       191 return '' unless $remain;
449 0         0 return "\0" x ($wantbytes - $remain);
450             }
451              
452             sub diffdata {
453 64     64   52 my $currdata = $data . '|';
454 64 50       112 return ($currdata eq $lastline) ? 0 : 1;
455             }
456              
457             sub help {
458 0     0     print "usage: od [-aBbcDdeFfHhilOosXxv] [-A radix] [-j skip_bytes] ",
459             "[-N limit_bytes] [-t type] [file]...\n";
460 0           exit EX_FAILURE;
461             }
462             __END__