#!perl -w
- # shasum: filter for computing SHA digests (analogous to sha1sum)
- #
- # Copyright (C) 2003-2010 Mark Shelor, All Rights Reserved
- #
- # Version: 5.48
- # Mon Jan 4 16:32:52 MST 2010
+ ## shasum: filter for computing SHA digests (ref. sha1sum/md5sum)
+ ##
+ ## Copyright (C) 2003-2010 Mark Shelor, All Rights Reserved
+ ##
+ ## Version: 5.49
+ ## Sun Dec 12 07:22:04 MST 2010
=head1 NAME
use FileHandle;
use Getopt::Long;
-my $VERSION = "5.48";
+my $VERSION = "5.49";
- # Try to use Digest::SHA, since it's faster. If not installed,
- # use Digest::SHA::PurePerl instead.
+ ## Try to use Digest::SHA, since it's faster. If not installed,
+ ## use Digest::SHA::PurePerl instead.
my $MOD_PREFER = "Digest::SHA";
my $MOD_SECOND = "Digest::SHA::PurePerl";
}
- # Usage statement adapted from Ulrich Drepper's md5sum.
- # Include an "-a" option for algorithm selection,
- # and a "-p" option for portable digest computation.
+ ## Usage statement adapted from Ulrich Drepper's md5sum.
+ ## Include an "-a" option for algorithm selection,
+ ## and a "-p" option for portable digest computation.
sub usage {
my($err, $msg) = @_;
}
- # Collect options from command line
+ ## Sync stdout and stderr by forcing a flush after every write
+
+autoflush STDOUT 1;
+autoflush STDERR 1;
+
+
+ ## Collect options from command line
my ($alg, $binary, $check, $text, $status, $warn, $help, $version);
my ($portable);
) or usage(1, "");
- # Deal with help requests and incorrect uses
+ ## Deal with help requests and incorrect uses
usage(0)
if $help;
if $status && !$check;
- # Default to SHA-1 unless overriden by command line option
+ ## Default to SHA-1 unless overriden by command line option
$alg = 1 unless defined $alg;
grep { $_ == $alg } (1, 224, 256, 384, 512)
or usage(1, "shasum: Unrecognized algorithm\n");
- # Display version information if requested
+ ## Display version information if requested
if ($version) {
print "$VERSION\n";
}
- # Try to figure out if the OS is DOS-like. If it is,
- # default to binary mode when reading files, unless
- # explicitly overriden by command line "--text" or
- # "--portable" options.
+ ## Try to figure out if the OS is DOS-like. If it is,
+ ## default to binary mode when reading files, unless
+ ## explicitly overriden by command line "--text" or
+ ## "--portable" options.
my $isDOSish = ($^O =~ /^(MSWin\d\d|os2|dos|mint|cygwin)$/);
if ($isDOSish) { $binary = 1 unless $text || $portable }
my $modesym = $binary ? '*' : ($portable ? '?' : ' ');
- # Read from STDIN (-) if no files listed on command line
+ ## Read from STDIN (-) if no files listed on command line
@ARGV = ("-") unless @ARGV;
- # sumfile($file): computes SHA digest of $file
+ ## sumfile($file): computes SHA digest of $file
sub sumfile {
my $file = shift;
my $mode = $portable ? 'p' : ($binary ? 'b' : '');
my $digest = eval { $module->new($alg)->addfile($file, $mode) };
- if ($@) {
- warn "shasum: $file: $!\n";
- return;
- }
-
+ if ($@) { warn "shasum: $file: $!\n"; return }
$digest->hexdigest;
}
- # %len2alg: maps hex digest length to SHA algorithm
+ ## %len2alg: maps hex digest length to SHA algorithm
my %len2alg = (40 => 1, 56 => 224, 64 => 256, 96 => 384, 128 => 512);
- # Verify checksums if requested
+ ## unescape: convert backslashed filename to plain filename
-if ($check) {
- my $checkfile = shift(@ARGV);
- my ($err, $read_errs, $match_errs) = (0, 0, 0);
- my ($num_files, $num_checksums) = (0, 0);
- my ($fh, $sum, $fname, $rsp, $digest);
+sub unescape {
+ $_ = shift;
+ s/\\\\/\0/g;
+ s/\\n/\n/g;
+ return if /\\/;
+ s/\0/\\/g;
+ return $_;
+}
- die "shasum: $checkfile: $!\n"
- unless $fh = FileHandle->new($checkfile, "r");
- while (<$fh>) {
- s/\s+$//;
- ($sum, $modesym, $fname) = /^(\S+) (.)(.*)$/;
- ($binary, $portable, $text) =
- map { $_ eq $modesym } ('*', '?', ' ');
- unless ($alg = $len2alg{length($sum)}) {
+
+ ## 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, $num_checksums) = (0, 0, 0);
+ my ($bslash, $sum, $fname, $rsp, $digest);
+
+ local *FH;
+ unless (sysopen(FH, $checkfile, O_RDONLY)) {
+ unless ($checkfile eq '-' && open(FH, '<&STDIN')) {
+ _bail("Open failed");
+ }
+ $checkfile = 'standard input';
+ }
+ while (<FH>) {
+ next if /^#/; s/\n$//; s/^[ \t]+//; $num_lines++;
+ $bslash = s/^\\//;
+ ($sum, $modesym, $fname) =
+ /^([\da-fA-F]+)[ \t]([ *?])([^\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 checksum line\n") if $warn;
+ "formatted SHA$alg checksum line\n") if $warn;
+ $fmt_errs++;
next;
}
+ $fname =~ s/\r$// unless -e $fname;
$rsp = "$fname: "; $num_files++;
+ ($binary, $portable, $text) =
+ map { $_ eq $modesym } ('*', '?', ' ');
unless ($digest = sumfile($fname)) {
$rsp .= "FAILED open or read\n";
$err = 1; $read_errs++;
}
print $rsp unless $status;
}
- $fh->close;
- unless ($status) {
- warn("shasum: WARNING: $read_errs of $num_files listed " .
- "files could not be read\n") if $read_errs;
- warn("shasum: WARNING: $match_errs of $num_checksums " .
- "computed checksums did NOT match\n") if $match_errs;
+ close(FH);
+ unless ($num_files) {
+ $alg = 1 unless defined $alg;
+ warn("shasum: $checkfile: no properly formatted " .
+ "SHA$alg checksum lines found\n");
+ $err = 1;
}
- exit($err);
+ 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);
}
- # Compute and display SHA checksums of requested files
+ ## Verify or compute SHA checksums of requested files
my($file, $digest);
+my $STATUS = 0;
for $file (@ARGV) {
- if ($digest = sumfile($file)) {
+ 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)