3 ## shasum: filter for computing SHA digests (ref. sha1sum/md5sum)
5 ## Copyright (C) 2003-2014 Mark Shelor, All Rights Reserved
8 ## Sun Jun 1 00:15:44 MST 2014
10 ## shasum SYNOPSIS adapted from GNU Coreutils sha1sum. Add
11 ## "-a" option for algorithm selection,
12 ## "-U" option for Universal Newlines support,
13 ## "-0" option for reading bit strings, and
14 ## "-p" option for portable digests (to be deprecated).
21 my $POD = <<'END_OF_POD';
25 shasum - Print or Check SHA Checksums
29 Usage: shasum [OPTION]... [FILE]...
30 Print or check SHA checksums.
31 With no FILE, or when FILE is -, read standard input.
33 -a, --algorithm 1 (default), 224, 256, 384, 512, 512224, 512256
34 -b, --binary read in binary mode
35 -c, --check read SHA sums from the FILEs and check them
36 -t, --text read in text mode (default)
37 -U, --UNIVERSAL read in Universal Newlines mode
38 produces same digest on Windows/Unix/Mac
39 -0, --01 read in BITS mode
40 ASCII '0' interpreted as 0-bit,
41 ASCII '1' interpreted as 1-bit,
42 all other characters ignored
43 -p, --portable read in portable mode (to be deprecated)
45 The following two options are useful only when verifying checksums:
46 -s, --status don't output anything, status code shows success
47 -w, --warn warn about improperly formatted checksum lines
49 -h, --help display this help and exit
50 -v, --version output version information and exit
52 When verifying SHA-512/224 or SHA-512/256 checksums, indicate the
53 algorithm explicitly using the -a option, e.g.
55 shasum -a 512224 -c checksumfile
57 The sums are computed as described in FIPS PUB 180-4. When checking,
58 the input should be a former output of this program. The default
59 mode is to print a line with checksum, a character indicating type
60 (`*' for binary, ` ' for text, `U' for UNIVERSAL, `^' for BITS, `?'
61 for portable), and name for each FILE.
63 Report shasum bugs to mshelor@cpan.org
67 Running I<shasum> is often the quickest way to compute SHA message
68 digests. The user simply feeds data to the script through files or
69 standard input, and then collects the results from standard output.
71 The following command shows how to compute digests for typical inputs
72 such as the NIST test vector "abc":
74 perl -e "print qq(abc)" | shasum
76 Or, if you want to use SHA-256 instead of the default SHA-1, simply say:
78 perl -e "print qq(abc)" | shasum -a 256
80 Since I<shasum> mimics the behavior of the combined GNU I<sha1sum>,
81 I<sha224sum>, I<sha256sum>, I<sha384sum>, and I<sha512sum> programs,
82 you can install this script as a convenient drop-in replacement.
84 Unlike the GNU programs, I<shasum> encompasses the full SHA standard by
85 allowing partial-byte inputs. This is accomplished through the BITS
86 option (I<-0>). The following example computes the SHA-224 digest of
87 the 7-bit message I<0001100>:
89 perl -e "print qq(0001100)" | shasum -0 -a 224
93 Copyright (c) 2003-2014 Mark Shelor <mshelor@cpan.org>.
97 I<shasum> is implemented using the Perl module L<Digest::SHA> or
98 L<Digest::SHA::PurePerl>.
104 my $VERSION = "5.92";
109 $msg = "" unless defined $msg;
111 warn($msg . "Type shasum -h for help\n");
114 my($USAGE) = $POD =~ /SYNOPSIS(.+?)^=/sm;
123 ## Sync stdout and stderr by forcing a flush after every write
125 select((select(STDOUT), $| = 1)[0]);
126 select((select(STDERR), $| = 1)[0]);
129 ## Collect options from command line
131 my ($alg, $binary, $check, $text, $status, $warn, $help, $version);
132 my ($portable, $BITS, $reverse, $UNIVERSAL, $versions);
134 eval { Getopt::Long::Configure ("bundling") };
136 'b|binary' => \$binary, 'c|check' => \$check,
137 't|text' => \$text, 'a|algorithm=i' => \$alg,
138 's|status' => \$status, 'w|warn' => \$warn,
139 'h|help' => \$help, 'v|version' => \$version,
140 'p|portable' => \$portable,
142 'R|REVERSE' => \$reverse,
143 'U|UNIVERSAL' => \$UNIVERSAL,
144 'V|VERSIONS' => \$versions,
148 ## Deal with help requests and incorrect uses
152 usage(1, "shasum: Ambiguous file mode\n")
153 if scalar(grep {defined $_}
154 ($binary, $portable, $text, $BITS, $UNIVERSAL)) > 1;
155 usage(1, "shasum: --warn option used only when verifying checksums\n")
157 usage(1, "shasum: --status option used only when verifying checksums\n")
158 if $status && !$check;
161 ## Try to use Digest::SHA. If not installed, use the slower
162 ## but functionally equivalent Digest::SHA::PurePerl instead.
164 ## If option -R is invoked, reverse the module preference,
165 ## i.e. try Digest::SHA::PurePerl first, then Digest::SHA.
167 my @MODS = qw(Digest::SHA Digest::SHA::PurePerl);
168 @MODS[0, 1] = @MODS[1, 0] if $reverse;
173 if (eval "require $mod") {
178 die "shasum: Unable to find " . join(" or ", @MODS) . "\n"
179 unless defined $module;
182 ## Default to SHA-1 unless overridden by command line option
184 $alg = 1 unless defined $alg;
185 grep { $_ == $alg } (1, 224, 256, 384, 512, 512224, 512256)
186 or usage(1, "shasum: Unrecognized algorithm\n");
189 ## Display version information if requested
197 print "shasum $VERSION\n";
198 print "$module ", eval "\$${module}::VERSION", "\n";
199 print "perl ", defined $^V ? sprintf("%vd", $^V) : $], "\n";
204 ## Try to figure out if the OS is DOS-like. If it is,
205 ## default to binary mode when reading files, unless
206 ## explicitly overridden by command line "--text" or
207 ## "--UNIVERSAL" or "--portable" options.
209 my $isDOSish = ($^O =~ /^(MSWin\d\d|os2|dos|mint|cygwin)$/);
210 if ($isDOSish) { $binary = 1 unless $text || $UNIVERSAL || $portable }
212 my $modesym = $binary ? '*' : ($UNIVERSAL ? 'U' :
213 ($BITS ? '^' : ($portable ? '?' : ' ')));
216 ## Read from STDIN (-) if no files listed on command line
218 @ARGV = ("-") unless @ARGV;
221 ## sumfile($file): computes SHA digest of $file
226 my $mode = $binary ? 'b' : ($UNIVERSAL ? 'U' :
227 ($BITS ? '0' : ($portable ? 'p' : '')));
228 my $digest = eval { $module->new($alg)->addfile($file, $mode) };
229 if ($@) { warn "shasum: $file: $!\n"; return }
234 ## %len2alg: maps hex digest length to SHA algorithm
236 my %len2alg = (40 => 1, 56 => 224, 64 => 256, 96 => 384, 128 => 512);
237 $len2alg{56} = 512224 if $alg == 512224;
238 $len2alg{64} = 512256 if $alg == 512256;
241 ## unescape: convert backslashed filename to plain filename
253 ## verify: confirm the digest values in a checksum file
256 my $checkfile = shift;
257 my ($err, $fmt_errs, $read_errs, $match_errs) = (0, 0, 0, 0);
258 my ($num_lines, $num_files) = (0, 0);
259 my ($bslash, $sum, $fname, $rsp, $digest);
262 $checkfile eq '-' and open(FH, '< -')
263 and $checkfile = 'standard input'
264 or sysopen(FH, $checkfile, O_RDONLY)
265 or die "shasum: $checkfile: $!\n";
267 next if /^#/; s/\n$//; s/^[ \t]+//; $num_lines++;
269 ($sum, $modesym, $fname) =
270 /^([\da-fA-F]+)[ \t]([ *?^U])([^\0]*)/;
271 $alg = defined $sum ? $len2alg{length($sum)} : undef;
272 $fname = unescape($fname) if defined $fname && $bslash;
273 if (grep { ! defined $_ } ($alg, $sum, $modesym, $fname)) {
274 $alg = 1 unless defined $alg;
275 warn("shasum: $checkfile: $.: improperly " .
276 "formatted SHA$alg checksum line\n") if $warn;
280 $fname =~ s/\r$// unless -e $fname;
281 $rsp = "$fname: "; $num_files++;
282 ($binary, $text, $UNIVERSAL, $BITS, $portable) =
283 map { $_ eq $modesym } ('*', ' ', 'U', '^', 'p');
284 unless ($digest = sumfile($fname)) {
285 $rsp .= "FAILED open or read\n";
286 $err = 1; $read_errs++;
289 if (lc($sum) eq $digest) { $rsp .= "OK\n" }
290 else { $rsp .= "FAILED\n"; $err = 1; $match_errs++ }
292 print $rsp unless $status;
295 unless ($num_files) {
296 $alg = 1 unless defined $alg;
297 warn("shasum: $checkfile: no properly formatted " .
298 "SHA$alg checksum lines found\n");
302 warn("shasum: WARNING: $fmt_errs line" . ($fmt_errs>1?
303 's are':' is') . " improperly formatted\n") if $fmt_errs;
304 warn("shasum: WARNING: $read_errs listed file" .
305 ($read_errs>1?'s':'') . " could not be read\n") if $read_errs;
306 warn("shasum: WARNING: $match_errs computed checksum" .
307 ($match_errs>1?'s':'') . " did NOT match\n") if $match_errs;
313 ## Verify or compute SHA checksums of requested files
319 if ($check) { $STATUS = 1 unless verify($file) }
320 elsif ($digest = sumfile($file)) {
321 if ($file =~ /[\n\\]/) {
322 $file =~ s/\\/\\\\/g; $file =~ s/\n/\\n/g;
323 $digest = "\\$digest";
325 print "$digest $modesym", "$file\n";