335 lines
9.2 KiB
Perl
335 lines
9.2 KiB
Perl
#!/usr/bin/perl
|
|
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
|
|
if $running_under_some_shell;
|
|
#!perl
|
|
|
|
## shasum: filter for computing SHA digests (ref. sha1sum/md5sum)
|
|
##
|
|
## Copyright (C) 2003-2016 Mark Shelor, All Rights Reserved
|
|
##
|
|
## Version: 5.96
|
|
## Wed Jul 27 20:04:34 MST 2016
|
|
|
|
## shasum SYNOPSIS adapted from GNU Coreutils sha1sum. Add
|
|
## "-a" option for algorithm selection,
|
|
## "-U" option for Universal Newlines support,
|
|
## "-0" option for reading bit strings, and
|
|
## "-p" option for portable digests (to be deprecated).
|
|
|
|
BEGIN { pop @INC if $INC[-1] eq '.' }
|
|
|
|
use strict;
|
|
use warnings;
|
|
use Fcntl;
|
|
use Getopt::Long;
|
|
|
|
my $POD = <<'END_OF_POD';
|
|
|
|
=head1 NAME
|
|
|
|
shasum - Print or Check SHA Checksums
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
Usage: shasum [OPTION]... [FILE]...
|
|
Print or check SHA checksums.
|
|
With no FILE, or when FILE is -, read standard input.
|
|
|
|
-a, --algorithm 1 (default), 224, 256, 384, 512, 512224, 512256
|
|
-b, --binary read in binary mode
|
|
-c, --check read SHA sums from the FILEs and check them
|
|
-t, --text read in text mode (default)
|
|
-U, --UNIVERSAL read in Universal Newlines mode
|
|
produces same digest on Windows/Unix/Mac
|
|
-0, --01 read in BITS mode
|
|
ASCII '0' interpreted as 0-bit,
|
|
ASCII '1' interpreted as 1-bit,
|
|
all other characters ignored
|
|
-p, --portable read in portable mode (to be deprecated)
|
|
|
|
The following two options are useful only when verifying checksums:
|
|
-s, --status don't output anything, status code shows success
|
|
-w, --warn warn about improperly formatted checksum lines
|
|
|
|
-h, --help display this help and exit
|
|
-v, --version output version information and exit
|
|
|
|
When verifying SHA-512/224 or SHA-512/256 checksums, indicate the
|
|
algorithm explicitly using the -a option, e.g.
|
|
|
|
shasum -a 512224 -c checksumfile
|
|
|
|
The sums are computed as described in FIPS PUB 180-4. When checking,
|
|
the input should be a former output of this program. The default
|
|
mode is to print a line with checksum, a character indicating type
|
|
(`*' for binary, ` ' for text, `U' for UNIVERSAL, `^' for BITS, `?'
|
|
for portable), and name for each FILE.
|
|
|
|
Report shasum bugs to mshelor@cpan.org
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Running I<shasum> is often the quickest way to compute SHA message
|
|
digests. The user simply feeds data to the script through files or
|
|
standard input, and then collects the results from standard output.
|
|
|
|
The following command shows how to compute digests for typical inputs
|
|
such as the NIST test vector "abc":
|
|
|
|
perl -e "print qq(abc)" | shasum
|
|
|
|
Or, if you want to use SHA-256 instead of the default SHA-1, simply say:
|
|
|
|
perl -e "print qq(abc)" | shasum -a 256
|
|
|
|
Since I<shasum> mimics the behavior of the combined GNU I<sha1sum>,
|
|
I<sha224sum>, I<sha256sum>, I<sha384sum>, and I<sha512sum> programs,
|
|
you can install this script as a convenient drop-in replacement.
|
|
|
|
Unlike the GNU programs, I<shasum> encompasses the full SHA standard by
|
|
allowing partial-byte inputs. This is accomplished through the BITS
|
|
option (I<-0>). The following example computes the SHA-224 digest of
|
|
the 7-bit message I<0001100>:
|
|
|
|
perl -e "print qq(0001100)" | shasum -0 -a 224
|
|
|
|
=head1 AUTHOR
|
|
|
|
Copyright (c) 2003-2016 Mark Shelor <mshelor@cpan.org>.
|
|
|
|
=head1 SEE ALSO
|
|
|
|
I<shasum> is implemented using the Perl module L<Digest::SHA> or
|
|
L<Digest::SHA::PurePerl>.
|
|
|
|
=cut
|
|
|
|
END_OF_POD
|
|
|
|
my $VERSION = "5.96";
|
|
|
|
sub usage {
|
|
my($err, $msg) = @_;
|
|
|
|
$msg = "" unless defined $msg;
|
|
if ($err) {
|
|
warn($msg . "Type shasum -h for help\n");
|
|
exit($err);
|
|
}
|
|
my($USAGE) = $POD =~ /SYNOPSIS(.+?)^=/sm;
|
|
$USAGE =~ s/^\s*//;
|
|
$USAGE =~ s/\s*$//;
|
|
$USAGE =~ s/^ //gm;
|
|
print $USAGE, "\n";
|
|
exit($err);
|
|
}
|
|
|
|
|
|
## Sync stdout and stderr by forcing a flush after every write
|
|
|
|
select((select(STDOUT), $| = 1)[0]);
|
|
select((select(STDERR), $| = 1)[0]);
|
|
|
|
|
|
## Collect options from command line
|
|
|
|
my ($alg, $binary, $check, $text, $status, $warn, $help, $version);
|
|
my ($portable, $BITS, $reverse, $UNIVERSAL, $versions);
|
|
|
|
eval { Getopt::Long::Configure ("bundling") };
|
|
GetOptions(
|
|
'b|binary' => \$binary, 'c|check' => \$check,
|
|
't|text' => \$text, 'a|algorithm=i' => \$alg,
|
|
's|status' => \$status, 'w|warn' => \$warn,
|
|
'h|help' => \$help, 'v|version' => \$version,
|
|
'p|portable' => \$portable,
|
|
'0|01' => \$BITS,
|
|
'R|REVERSE' => \$reverse,
|
|
'U|UNIVERSAL' => \$UNIVERSAL,
|
|
'V|VERSIONS' => \$versions,
|
|
) or usage(1, "");
|
|
|
|
|
|
## Deal with help requests and incorrect uses
|
|
|
|
usage(0)
|
|
if $help;
|
|
usage(1, "shasum: Ambiguous file mode\n")
|
|
if scalar(grep {defined $_}
|
|
($binary, $portable, $text, $BITS, $UNIVERSAL)) > 1;
|
|
usage(1, "shasum: --warn option used only when verifying checksums\n")
|
|
if $warn && !$check;
|
|
usage(1, "shasum: --status option used only when verifying checksums\n")
|
|
if $status && !$check;
|
|
|
|
|
|
## Try to use Digest::SHA. If not installed, use the slower
|
|
## but functionally equivalent Digest::SHA::PurePerl instead.
|
|
|
|
## If option -R is invoked, reverse the module preference,
|
|
## i.e. try Digest::SHA::PurePerl first, then Digest::SHA.
|
|
|
|
my @MODS = qw(Digest::SHA Digest::SHA::PurePerl);
|
|
@MODS[0, 1] = @MODS[1, 0] if $reverse;
|
|
|
|
my $module;
|
|
for (@MODS) {
|
|
my $mod = $_;
|
|
if (eval "require $mod") {
|
|
$module = $mod;
|
|
last;
|
|
}
|
|
}
|
|
die "shasum: Unable to find " . join(" or ", @MODS) . "\n"
|
|
unless defined $module;
|
|
|
|
|
|
## Default to SHA-1 unless overridden by command line option
|
|
|
|
$alg = 1 unless defined $alg;
|
|
grep { $_ == $alg } (1, 224, 256, 384, 512, 512224, 512256)
|
|
or usage(1, "shasum: Unrecognized algorithm\n");
|
|
|
|
|
|
## Display version information if requested
|
|
|
|
if ($version) {
|
|
print "$VERSION\n";
|
|
exit(0);
|
|
}
|
|
|
|
if ($versions) {
|
|
print "shasum $VERSION\n";
|
|
print "$module ", eval "\$${module}::VERSION", "\n";
|
|
print "perl ", defined $^V ? sprintf("%vd", $^V) : $], "\n";
|
|
exit(0);
|
|
}
|
|
|
|
|
|
## Try to figure out if the OS is DOS-like. If it is,
|
|
## default to binary mode when reading files, unless
|
|
## explicitly overridden by command line "--text" or
|
|
## "--UNIVERSAL" or "--portable" options.
|
|
|
|
my $isDOSish = ($^O =~ /^(MSWin\d\d|os2|dos|mint|cygwin|msys)$/);
|
|
if ($isDOSish) { $binary = 1 unless $text || $UNIVERSAL || $portable }
|
|
|
|
my $modesym = $binary ? '*' : ($UNIVERSAL ? 'U' :
|
|
($BITS ? '^' : ($portable ? '?' : ' ')));
|
|
|
|
|
|
## Read from STDIN (-) if no files listed on command line
|
|
|
|
@ARGV = ("-") unless @ARGV;
|
|
|
|
|
|
## sumfile($file): computes SHA digest of $file
|
|
|
|
sub sumfile {
|
|
my $file = shift;
|
|
|
|
my $mode = $binary ? 'b' : ($UNIVERSAL ? 'U' :
|
|
($BITS ? '0' : ($portable ? 'p' : '')));
|
|
my $digest = eval { $module->new($alg)->addfile($file, $mode) };
|
|
if ($@) { warn "shasum: $file: $!\n"; return }
|
|
$digest->hexdigest;
|
|
}
|
|
|
|
|
|
## %len2alg: maps hex digest length to SHA algorithm
|
|
|
|
my %len2alg = (40 => 1, 56 => 224, 64 => 256, 96 => 384, 128 => 512);
|
|
$len2alg{56} = 512224 if $alg == 512224;
|
|
$len2alg{64} = 512256 if $alg == 512256;
|
|
|
|
|
|
## unescape: convert backslashed filename to plain filename
|
|
|
|
sub unescape {
|
|
$_ = shift;
|
|
s/\\\\/\0/g;
|
|
s/\\n/\n/g;
|
|
return if /\\/;
|
|
s/\0/\\/g;
|
|
return $_;
|
|
}
|
|
|
|
|
|
## verify: confirm the digest values in a checksum file
|
|
|
|
sub verify {
|
|
my $checkfile = shift;
|
|
my ($err, $fmt_errs, $read_errs, $match_errs) = (0, 0, 0, 0);
|
|
my ($num_lines, $num_files) = (0, 0);
|
|
my ($bslash, $sum, $fname, $rsp, $digest);
|
|
|
|
local *FH;
|
|
$checkfile eq '-' and open(FH, '< -')
|
|
and $checkfile = 'standard input'
|
|
or sysopen(FH, $checkfile, O_RDONLY)
|
|
or die "shasum: $checkfile: $!\n";
|
|
while (<FH>) {
|
|
next if /^#/; s/\n$//; s/^[ \t]+//; $num_lines++;
|
|
$bslash = s/^\\//;
|
|
($sum, $modesym, $fname) =
|
|
/^([\da-fA-F]+)[ \t]([ *?^U])([^\0]*)/;
|
|
$alg = defined $sum ? $len2alg{length($sum)} : undef;
|
|
$fname = unescape($fname) if defined $fname && $bslash;
|
|
if (grep { ! defined $_ } ($alg, $sum, $modesym, $fname)) {
|
|
$alg = 1 unless defined $alg;
|
|
warn("shasum: $checkfile: $.: improperly " .
|
|
"formatted SHA$alg checksum line\n") if $warn;
|
|
$fmt_errs++;
|
|
next;
|
|
}
|
|
$fname =~ s/\r$// unless -e $fname;
|
|
$rsp = "$fname: "; $num_files++;
|
|
($binary, $text, $UNIVERSAL, $BITS, $portable) =
|
|
map { $_ eq $modesym } ('*', ' ', 'U', '^', 'p');
|
|
unless ($digest = sumfile($fname)) {
|
|
$rsp .= "FAILED open or read\n";
|
|
$err = 1; $read_errs++;
|
|
}
|
|
else {
|
|
if (lc($sum) eq $digest) { $rsp .= "OK\n" }
|
|
else { $rsp .= "FAILED\n"; $err = 1; $match_errs++ }
|
|
}
|
|
print $rsp unless $status;
|
|
}
|
|
close(FH);
|
|
unless ($num_files) {
|
|
$alg = 1 unless defined $alg;
|
|
warn("shasum: $checkfile: no properly formatted " .
|
|
"SHA$alg checksum lines found\n");
|
|
$err = 1;
|
|
}
|
|
elsif (! $status) {
|
|
warn("shasum: WARNING: $fmt_errs line" . ($fmt_errs>1?
|
|
's are':' is') . " improperly formatted\n") if $fmt_errs;
|
|
warn("shasum: WARNING: $read_errs listed file" .
|
|
($read_errs>1?'s':'') . " could not be read\n") if $read_errs;
|
|
warn("shasum: WARNING: $match_errs computed checksum" .
|
|
($match_errs>1?'s':'') . " did NOT match\n") if $match_errs;
|
|
}
|
|
return($err == 0);
|
|
}
|
|
|
|
|
|
## Verify or compute SHA checksums of requested files
|
|
|
|
my($file, $digest);
|
|
|
|
my $STATUS = 0;
|
|
for $file (@ARGV) {
|
|
if ($check) { $STATUS = 1 unless verify($file) }
|
|
elsif ($digest = sumfile($file)) {
|
|
if ($file =~ /[\n\\]/) {
|
|
$file =~ s/\\/\\\\/g; $file =~ s/\n/\\n/g;
|
|
$digest = "\\$digest";
|
|
}
|
|
print "$digest $modesym", "$file\n";
|
|
}
|
|
else { $STATUS = 1 }
|
|
}
|
|
exit($STATUS)
|