This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Update Digest-SHA to CPAN version 5.73
[perl5.git] / cpan / Digest-SHA / shasum
1 #!perl -w
2
3         ## shasum: filter for computing SHA digests (ref. sha1sum/md5sum)
4         ##
5         ## Copyright (C) 2003-2012 Mark Shelor, All Rights Reserved
6         ##
7         ## Version: 5.73
8         ## Wed Oct 31 04:32:44 MST 2012
9
10         ## shasum SYNOPSIS adapted from GNU Coreutils sha1sum.
11         ## Add an "-a" option for algorithm selection, a "-p"
12         ## option for portable digest computation, and a "-0"
13         ## option for reading bit strings.
14
15 my $POD = <<'END_OF_POD';
16
17 =head1 NAME
18
19 shasum - Print or Check SHA Checksums
20
21 =head1 SYNOPSIS
22
23  Usage: shasum [OPTION]... [FILE]...
24  Print or check SHA checksums.
25  With no FILE, or when FILE is -, read standard input.
26
27    -a, --algorithm   1 (default), 224, 256, 384, 512, 512224, 512256
28    -b, --binary      read in binary mode
29    -c, --check       read SHA sums from the FILEs and check them
30    -t, --text        read in text mode (default)
31    -p, --portable    read in portable mode
32                          produces same digest on Windows/Unix/Mac
33    -0, --01          read in BITS mode
34                          ASCII '0' interpreted as 0-bit,
35                          ASCII '1' interpreted as 1-bit,
36                          all other characters ignored
37
38  The following two options are useful only when verifying checksums:
39    -s, --status      don't output anything, status code shows success
40    -w, --warn        warn about improperly formatted checksum lines
41
42    -h, --help        display this help and exit
43    -v, --version     output version information and exit
44
45  When verifying SHA-512/224 or SHA-512/256 checksums, indicate the
46  algorithm explicitly using the -a option, e.g.
47
48    shasum -a 512224 -c checksumfile
49
50  The sums are computed as described in FIPS-180-4.  When checking, the
51  input should be a former output of this program.  The default mode is to
52  print a line with checksum, a character indicating type (`*' for binary,
53  ` ' for text, `?' for portable, `^' for BITS), and name for each FILE.
54
55  Report shasum bugs to mshelor@cpan.org
56
57 =head1 DESCRIPTION
58
59 Running I<shasum> is often the quickest way to compute SHA message
60 digests.  The user simply feeds data to the script through files or
61 standard input, and then collects the results from standard output.
62
63 The following command shows how to compute digests for typical inputs
64 such as the NIST test vector "abc":
65
66         perl -e "print qq(abc)" | shasum
67
68 Or, if you want to use SHA-256 instead of the default SHA-1, simply say:
69
70         perl -e "print qq(abc)" | shasum -a 256
71
72 Since I<shasum> mimics the behavior of the combined GNU I<sha1sum>,
73 I<sha224sum>, I<sha256sum>, I<sha384sum>, and I<sha512sum> programs,
74 you can install this script as a convenient drop-in replacement.
75
76 Unlike the GNU programs, I<shasum> encompasses the full SHA standard by
77 allowing partial-byte inputs.  This is accomplished through the BITS
78 option (I<-0>).  The following example computes the SHA-224 digest of
79 the 7-bit message I<0001100>:
80
81         perl -e "print qq(0001100)" | shasum -0 -a 224
82
83 =head1 AUTHOR
84
85 Copyright (c) 2003-2012 Mark Shelor <mshelor@cpan.org>.
86
87 =head1 SEE ALSO
88
89 I<shasum> is implemented using the Perl module L<Digest::SHA> or
90 L<Digest::SHA::PurePerl>.
91
92 =cut
93
94 END_OF_POD
95
96 use strict;
97 use Fcntl;
98 use Getopt::Long;
99
100 my $VERSION = "5.73";
101
102
103         ## Try to use Digest::SHA.  If not installed, use the slower
104         ## but functionally equivalent Digest::SHA::PurePerl instead.
105
106 my $MOD_PREFER = "Digest::SHA";
107 my $MOD_SECOND = "Digest::SHA::PurePerl";
108
109 my $module = $MOD_PREFER;
110 eval "require $module";
111 if ($@) {
112         $module = $MOD_SECOND;
113         eval "require $module";
114         die "Unable to find $MOD_PREFER or $MOD_SECOND\n" if $@;
115 }
116
117
118 sub usage {
119         my($err, $msg) = @_;
120
121         $msg = "" unless defined $msg;
122         if ($err) {
123                 warn($msg . "Type shasum -h for help\n");
124                 exit($err);
125         }
126         my($USAGE) = $POD =~ /SYNOPSIS\n\n(.+)\n=head1 DESCRIPTION\n/sm;
127         $USAGE =~ s/^ //gm;
128         print $USAGE;
129         exit($err);
130 }
131
132
133         ## Sync stdout and stderr by forcing a flush after every write
134
135 select((select(STDOUT), $| = 1)[0]);
136 select((select(STDERR), $| = 1)[0]);
137
138
139         ## Collect options from command line
140
141 my ($alg, $binary, $check, $text, $status, $warn, $help, $version);
142 my ($portable, $BITS);
143
144 eval { Getopt::Long::Configure ("bundling") };
145 GetOptions(
146         'b|binary' => \$binary, 'c|check' => \$check,
147         't|text' => \$text, 'a|algorithm=i' => \$alg,
148         's|status' => \$status, 'w|warn' => \$warn,
149         'h|help' => \$help, 'v|version' => \$version,
150         'p|portable' => \$portable,
151         '0|01' => \$BITS
152 ) or usage(1, "");
153
154
155         ## Deal with help requests and incorrect uses
156
157 usage(0)
158         if $help;
159 usage(1, "shasum: Ambiguous file mode\n")
160         if scalar(grep {defined $_} ($binary, $portable, $text, $BITS)) > 1;
161 usage(1, "shasum: --warn option used only when verifying checksums\n")
162         if $warn && !$check;
163 usage(1, "shasum: --status option used only when verifying checksums\n")
164         if $status && !$check;
165
166
167         ## Default to SHA-1 unless overriden by command line option
168
169 $alg = 1 unless defined $alg;
170 grep { $_ == $alg } (1, 224, 256, 384, 512, 512224, 512256)
171         or usage(1, "shasum: Unrecognized algorithm\n");
172
173
174         ## Display version information if requested
175
176 if ($version) {
177         print "$VERSION\n";
178         exit(0);
179 }
180
181
182         ## Try to figure out if the OS is DOS-like.  If it is,
183         ## default to binary mode when reading files, unless
184         ## explicitly overriden by command line "--text" or
185         ## "--portable" options.
186
187 my $isDOSish = ($^O =~ /^(MSWin\d\d|os2|dos|mint|cygwin)$/);
188 if ($isDOSish) { $binary = 1 unless $text || $portable }
189
190 my $modesym = $binary ? '*' : ($portable ? '?' : ($BITS ? '^' : ' '));
191
192
193         ## Read from STDIN (-) if no files listed on command line
194
195 @ARGV = ("-") unless @ARGV;
196
197
198         ## sumfile($file): computes SHA digest of $file
199
200 sub sumfile {
201         my $file = shift;
202
203         my $mode = $portable ? 'p' : ($binary ? 'b' : ($BITS ? '0' : ''));
204         my $digest = eval { $module->new($alg)->addfile($file, $mode) };
205         if ($@) { warn "shasum: $file: $!\n"; return }
206         $digest->hexdigest;
207 }
208
209
210         ## %len2alg: maps hex digest length to SHA algorithm
211
212 my %len2alg = (40 => 1, 56 => 224, 64 => 256, 96 => 384, 128 => 512);
213 $len2alg{56} = 512224 if $alg == 512224;
214 $len2alg{64} = 512256 if $alg == 512256;
215
216
217         ## unescape: convert backslashed filename to plain filename
218
219 sub unescape {
220         $_ = shift;
221         s/\\\\/\0/g;
222         s/\\n/\n/g;
223         return if /\\/;
224         s/\0/\\/g;
225         return $_;
226 }
227
228
229         ## verify: confirm the digest values in a checksum file
230
231 sub verify {
232         my $checkfile = shift;
233         my ($err, $fmt_errs, $read_errs, $match_errs) = (0, 0, 0, 0);
234         my ($num_lines, $num_files) = (0, 0);
235         my ($bslash, $sum, $fname, $rsp, $digest);
236
237         local *FH;
238         $checkfile eq '-' and open(FH, '< -')
239                 and $checkfile = 'standard input'
240         or sysopen(FH, $checkfile, O_RDONLY)
241                 or die "shasum: $checkfile: $!\n";
242         while (<FH>) {
243                 next if /^#/; s/\n$//; s/^[ \t]+//; $num_lines++;
244                 $bslash = s/^\\//;
245                 ($sum, $modesym, $fname) =
246                         /^([\da-fA-F]+)[ \t]([ *?^])([^\0]*)/;
247                 $alg = defined $sum ? $len2alg{length($sum)} : undef;
248                 $fname = unescape($fname) if defined $fname && $bslash;
249                 if (grep { ! defined $_ } ($alg, $sum, $modesym, $fname)) {
250                         $alg = 1 unless defined $alg;
251                         warn("shasum: $checkfile: $.: improperly " .
252                                 "formatted SHA$alg checksum line\n") if $warn;
253                         $fmt_errs++;
254                         next;
255                 }
256                 $fname =~ s/\r$// unless -e $fname;
257                 $rsp = "$fname: "; $num_files++;
258                 ($binary, $portable, $text, $BITS) =
259                         map { $_ eq $modesym } ('*', '?', ' ', '^');
260                 unless ($digest = sumfile($fname)) {
261                         $rsp .= "FAILED open or read\n";
262                         $err = 1; $read_errs++;
263                 }
264                 else {
265                         if (lc($sum) eq $digest) { $rsp .= "OK\n" }
266                         else { $rsp .= "FAILED\n"; $err = 1; $match_errs++ }
267                 }
268                 print $rsp unless $status;
269         }
270         close(FH);
271         unless ($num_files) {
272                 $alg = 1 unless defined $alg;
273                 warn("shasum: $checkfile: no properly formatted " .
274                         "SHA$alg checksum lines found\n");
275                 $err = 1;
276         }
277         elsif (! $status) {
278                 warn("shasum: WARNING: $fmt_errs line" . ($fmt_errs>1?
279                 's are':' is') . " improperly formatted\n") if $fmt_errs;
280                 warn("shasum: WARNING: $read_errs listed file" .
281                 ($read_errs>1?'s':'') . " could not be read\n") if $read_errs;
282                 warn("shasum: WARNING: $match_errs computed checksum" .
283                 ($match_errs>1?'s':'') . " did NOT match\n") if $match_errs;
284         }
285         return($err == 0);
286 }
287
288
289         ## Verify or compute SHA checksums of requested files
290
291 my($file, $digest);
292
293 my $STATUS = 0;
294 for $file (@ARGV) {
295         if ($check) { $STATUS = 1 unless verify($file) }
296         elsif ($digest = sumfile($file)) {
297                 if ($file =~ /[\n\\]/) {
298                         $file =~ s/\\/\\\\/g; $file =~ s/\n/\\n/g;
299                         $digest = "\\$digest";
300                 }
301                 print "$digest $modesym", "$file\n";
302         }
303         else { $STATUS = 1 }
304 }
305 exit($STATUS)