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