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