3 ## shasum: filter for computing SHA digests (ref. sha1sum/md5sum)
5 ## Copyright (C) 2003-2011 Mark Shelor, All Rights Reserved
8 ## Wed Mar 9 05:26:36 MST 2011
10 ## shasum SYNOPSIS adapted from GNU Coreutils sha1sum.
11 ## Include an "-a" option for algorithm selection, and a
12 ## "-p" option for portable digest computation.
14 my $POD = <<'END_OF_POD';
18 shasum - Print or Check SHA Checksums
22 Usage: shasum [OPTION]... [FILE]...
23 Print or check SHA checksums.
24 With no FILE, or when FILE is -, read standard input.
26 -a, --algorithm 1 (default), 224, 256, 384, 512, 512224, 512256
27 -b, --binary read in binary mode
28 -c, --check read SHA sums from the FILEs and check them
29 -p, --portable read files in portable mode
30 produces same digest on Windows/Unix/Mac
31 -t, --text read in text mode (default)
33 The following two options are useful only when verifying checksums:
34 -s, --status don't output anything, status code shows success
35 -w, --warn warn about improperly formatted checksum lines
37 -h, --help display this help and exit
38 -v, --version output version information and exit
40 When verifying SHA-512/224 or SHA-512/256 checksums, indicate the
41 algorithm explicitly using the -a option, e.g.
43 shasum -a 512224 -c checksumfile
45 The sums are computed as described in FIPS-180-4. When checking, the
46 input should be a former output of this program. The default mode is to
47 print a line with checksum, a character indicating type (`*' for binary,
48 ` ' for text, `?' for portable), and name for each FILE.
50 Report shasum bugs to mshelor@cpan.org
54 Running I<shasum> is often the quickest way to compute SHA message
55 digests. The user simply feeds data to the script through files or
56 standard input, and then collects the results from standard output.
58 The following command shows how easy it is to compute digests for typical
59 inputs such as the NIST test vector "abc":
61 perl -e "print qq(abc)" | shasum
63 Or, if you want to use SHA-256 instead of the default SHA-1, simply say:
65 perl -e "print qq(abc)" | shasum -a 256
67 Since I<shasum> mimics the behavior of the combined GNU I<sha1sum>,
68 I<sha224sum>, I<sha256sum>, I<sha384sum>, and I<sha512sum> programs,
69 you can install this script as a convenient drop-in replacement.
73 Copyright (c) 2003-2011 Mark Shelor <mshelor@cpan.org>.
77 I<shasum> is implemented using the Perl module L<Digest::SHA> or
78 L<Digest::SHA::PurePerl>.
91 ## Try to use Digest::SHA. If not installed, use the slower
92 ## but functionally equivalent Digest::SHA::PurePerl instead.
94 my $MOD_PREFER = "Digest::SHA";
95 my $MOD_SECOND = "Digest::SHA::PurePerl";
97 my $module = $MOD_PREFER;
98 eval "require $module";
100 $module = $MOD_SECOND;
101 eval "require $module";
102 die "Unable to find $MOD_PREFER or $MOD_SECOND\n" if $@;
109 $msg = "" unless defined $msg;
111 warn($msg . "Type shasum -h for help\n");
114 my($USAGE) = $POD =~ /SYNOPSIS\n\n(.+)\n=head1 DESCRIPTION\n/sm;
121 ## Sync stdout and stderr by forcing a flush after every write
123 select((select(STDOUT), $| = 1)[0]);
124 select((select(STDERR), $| = 1)[0]);
127 ## Collect options from command line
129 my ($alg, $binary, $check, $text, $status, $warn, $help, $version);
132 eval { Getopt::Long::Configure ("bundling") };
134 'b|binary' => \$binary, 'c|check' => \$check,
135 't|text' => \$text, 'a|algorithm=i' => \$alg,
136 's|status' => \$status, 'w|warn' => \$warn,
137 'h|help' => \$help, 'v|version' => \$version,
138 'p|portable' => \$portable
142 ## Deal with help requests and incorrect uses
146 usage(1, "shasum: Ambiguous file mode\n")
147 if scalar(grep { defined $_ } ($binary, $portable, $text)) > 1;
148 usage(1, "shasum: --warn option used only when verifying checksums\n")
150 usage(1, "shasum: --status option used only when verifying checksums\n")
151 if $status && !$check;
154 ## Default to SHA-1 unless overriden by command line option
156 $alg = 1 unless defined $alg;
157 grep { $_ == $alg } (1, 224, 256, 384, 512, 512224, 512256)
158 or usage(1, "shasum: Unrecognized algorithm\n");
161 ## Display version information if requested
169 ## Try to figure out if the OS is DOS-like. If it is,
170 ## default to binary mode when reading files, unless
171 ## explicitly overriden by command line "--text" or
172 ## "--portable" options.
174 my $isDOSish = ($^O =~ /^(MSWin\d\d|os2|dos|mint|cygwin)$/);
175 if ($isDOSish) { $binary = 1 unless $text || $portable }
177 my $modesym = $binary ? '*' : ($portable ? '?' : ' ');
180 ## Read from STDIN (-) if no files listed on command line
182 @ARGV = ("-") unless @ARGV;
185 ## sumfile($file): computes SHA digest of $file
190 my $mode = $portable ? 'p' : ($binary ? 'b' : '');
191 my $digest = eval { $module->new($alg)->addfile($file, $mode) };
192 if ($@) { warn "shasum: $file: $!\n"; return }
197 ## %len2alg: maps hex digest length to SHA algorithm
199 my %len2alg = (40 => 1, 56 => 224, 64 => 256, 96 => 384, 128 => 512);
200 $len2alg{56} = 512224 if $alg == 512224;
201 $len2alg{64} = 512256 if $alg == 512256;
204 ## unescape: convert backslashed filename to plain filename
216 ## verify: confirm the digest values in a checksum file
219 my $checkfile = shift;
220 my ($err, $fmt_errs, $read_errs, $match_errs) = (0, 0, 0, 0);
221 my ($num_lines, $num_files) = (0, 0);
222 my ($bslash, $sum, $fname, $rsp, $digest);
225 $checkfile eq '-' and open(FH, '< -')
226 and $checkfile = 'standard input'
227 or sysopen(FH, $checkfile, O_RDONLY)
228 or die "shasum: $checkfile: $!\n";
230 next if /^#/; s/\n$//; s/^[ \t]+//; $num_lines++;
232 ($sum, $modesym, $fname) =
233 /^([\da-fA-F]+)[ \t]([ *?])([^\0]*)/;
234 $alg = defined $sum ? $len2alg{length($sum)} : undef;
235 $fname = unescape($fname) if defined $fname && $bslash;
236 if (grep { ! defined $_ } ($alg, $sum, $modesym, $fname)) {
237 $alg = 1 unless defined $alg;
238 warn("shasum: $checkfile: $.: improperly " .
239 "formatted SHA$alg checksum line\n") if $warn;
243 $fname =~ s/\r$// unless -e $fname;
244 $rsp = "$fname: "; $num_files++;
245 ($binary, $portable, $text) =
246 map { $_ eq $modesym } ('*', '?', ' ');
247 unless ($digest = sumfile($fname)) {
248 $rsp .= "FAILED open or read\n";
249 $err = 1; $read_errs++;
252 if (lc($sum) eq $digest) { $rsp .= "OK\n" }
253 else { $rsp .= "FAILED\n"; $err = 1; $match_errs++ }
255 print $rsp unless $status;
258 unless ($num_files) {
259 $alg = 1 unless defined $alg;
260 warn("shasum: $checkfile: no properly formatted " .
261 "SHA$alg checksum lines found\n");
265 warn("shasum: WARNING: $fmt_errs line" . ($fmt_errs>1?
266 's are':' is') . " improperly formatted\n") if $fmt_errs;
267 warn("shasum: WARNING: $read_errs listed file" .
268 ($read_errs>1?'s':'') . " could not be read\n") if $read_errs;
269 warn("shasum: WARNING: $match_errs computed checksum" .
270 ($match_errs>1?'s':'') . " did NOT match\n") if $match_errs;
276 ## Verify or compute SHA checksums of requested files
282 if ($check) { $STATUS = 1 unless verify($file) }
283 elsif ($digest = sumfile($file)) {
284 if ($file =~ /[\n\\]/) {
285 $file =~ s/\\/\\\\/g; $file =~ s/\n/\\n/g;
286 $digest = "\\$digest";
288 print "$digest $modesym", "$file\n";