This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
fdb615b4fc1ab5983b86bf145284c48a98a2adff
[perl5.git] / ext / Digest / SHA / bin / shasum
1 #!perl -w
2
3         # shasum: filter for computing SHA digests (analogous to md5sum)
4         #
5         # Copyright (C) 2003-2006 Mark Shelor, All Rights Reserved
6         #
7         # Version: 5.38
8         # Thu May 25 02:02:02 MST 2006
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/MacOS 9
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 AUTHOR
42
43 Copyright (c) 2003-2006 Mark Shelor <mshelor@cpan.org>.
44
45 =head1 SEE ALSO
46
47 shasum is implemented using the Perl module L<Digest::SHA> or
48 L<Digest::SHA::PurePerl>.
49
50 =cut
51
52 use strict;
53 use Getopt::Long;
54
55 my $VERSION = "5.38";
56
57
58         # Try to use Digest::SHA, since it's faster.  If not installed,
59         # use Digest::SHA::PurePerl instead.
60
61 my $MOD_PREFER = "Digest::SHA";
62 my $MOD_SECOND = "Digest::SHA::PurePerl";
63
64 my $module = $MOD_PREFER;
65 eval "require $module";
66 if ($@) {
67         $module = $MOD_SECOND;
68         eval "require $module";
69         die "Unable to find $MOD_PREFER or $MOD_SECOND\n" if $@;
70 }
71
72
73         # Usage statement adapted from Ulrich Drepper's md5sum.
74         # Include an "-a" option for algorithm selection,
75         # and a "-p" option for portable digest computation.
76
77 sub usage {
78         my($err, $msg) = @_;
79
80         $msg = "" unless defined $msg;
81         if ($err) {
82                 warn($msg . "Type shasum -h for help\n");
83                 exit($err);
84         }
85         print <<'END_OF_USAGE';
86 Usage: shasum [OPTION] [FILE]...
87    or: shasum [OPTION] --check [FILE]
88 Print or check SHA checksums.
89 With no FILE, or when FILE is -, read standard input.
90
91   -a, --algorithm    1 (default), 224, 256, 384, 512
92   -b, --binary       read files in binary mode (default on DOS/Windows)
93   -c, --check        check SHA sums against given list
94   -p, --portable     read files in portable mode
95                          produces same digest on Windows/Unix/MacOS 9
96   -t, --text         read files in text mode (default)
97
98 The following two options are useful only when verifying checksums:
99   -s, --status       don't output anything, status code shows success
100   -w, --warn         warn about improperly formatted SHA checksum lines
101
102   -h, --help         display this help and exit
103   -v, --version      output version information and exit
104
105 The sums are computed as described in FIPS PUB 180-2.  When checking, the
106 input should be a former output of this program.  The default mode is to
107 print a line with checksum, a character indicating type (`*' for binary,
108 `?' for portable, ` ' for text), and name for each FILE.
109
110 Report bugs to <mshelor@cpan.org>.
111 END_OF_USAGE
112         exit($err);
113 }
114
115
116         # Collect options from command line
117
118 my ($alg, $binary, $check, $text, $status, $warn, $help, $version);
119 my ($portable);
120
121 Getopt::Long::Configure ("bundling");
122 GetOptions(
123         'b|binary' => \$binary, 'c|check' => \$check,
124         't|text' => \$text, 'a|algorithm=i' => \$alg,
125         's|status' => \$status, 'w|warn' => \$warn,
126         'h|help' => \$help, 'v|version' => \$version,
127         'p|portable' => \$portable
128 ) or usage(1, "");
129
130
131         # Deal with help requests and incorrect uses
132
133 usage(0)
134         if $help;
135 usage(1, "Ambiguous file mode\n")
136         if scalar(grep { defined $_ } ($binary, $portable, $text)) > 1;
137 usage(1, "shasum: --warn option used only when verifying checksums\n")
138         if $warn && !$check;
139 usage(1, "shasum: --status option used only when verifying checksums\n")
140         if $status && !$check;
141
142
143         # Default to SHA-1 unless overriden by command line option
144
145 $alg = 1 unless $alg;
146 grep { $_ == $alg } (1, 224, 256, 384, 512)
147         or usage(1, "Unrecognized algorithm\n");
148
149
150         # Display version information if requested
151
152 if ($version) {
153         print "$VERSION\n";
154         exit(0);
155 }
156
157
158         # Try to figure out if the OS is DOS-like.  If it is,
159         # default to binary mode when reading files, unless
160         # explicitly overriden by command line "--text" or
161         # "--portable" options.
162
163 my $isDOSish = ($^O =~ /^(MSWin\d\d|os2|dos|mint|cygwin)$/);
164 if ($isDOSish) { $binary = 1 unless $text || $portable }
165
166 my $modesym = $binary ? '*' : ($portable ? '?' : ' ');
167
168
169         # Read from STDIN (-) if no files listed on command line
170
171 @ARGV = ("-") unless @ARGV;
172
173
174         # sumfile($file): computes SHA digest of $file
175
176 sub sumfile {
177         my $file = shift;
178
179         unless (-e $file) {
180                 warn "shasum: $file: No such file or directory\n";
181                 return;
182         }
183         unless (-r $file) {
184                 warn "shasum: $file: FAILED open or read\n";
185                 return;
186         }
187
188         my $digest = $module->new($alg);
189         unless ($digest) {
190                 warn "shasum: $file: FAILED digest creation\n";
191                 return;
192         }
193
194         my $readmode = $portable ? 'p' : ($binary ? 'b' : '');
195
196         $digest->addfile($file, $readmode)->hexdigest;
197 }
198
199
200         # %len2alg: maps hex digest length to SHA algorithm
201
202 my %len2alg = (40 => 1, 56 => 224, 64 => 256, 96 => 384, 128 => 512);
203
204
205         # Verify checksums if requested
206
207 if ($check) {
208         my $checkfile = shift(@ARGV);
209         my ($err, $read_errs, $match_errs) = (0, 0, 0);
210         my ($num_files, $num_checksums) = (0, 0);
211         my ($fh, $sum, $fname, $rsp);
212
213         die "shasum: $checkfile: No such file or directory\n"
214                 unless open($fh, "<$checkfile");
215         while (<$fh>) {
216                 s/\s+$//;
217                 ($sum, $modesym, $fname) = /^(\S+) (.)(.*)$/;
218                 ($binary, $portable, $text) =
219                         map { $_ eq $modesym } ('*', '?', ' ');
220                 unless ($alg = $len2alg{length($sum)}) {
221                         warn("shasum: $checkfile: $.: improperly " .
222                                 "formatted SHA checksum line\n") if $warn;
223                         next;
224                 }
225                 $rsp = "$fname: "; $num_files++;
226                 unless (my $digest = sumfile($fname)) {
227                         $rsp .= "FAILED open or read\n";
228                         $err = 1; $read_errs++;
229                 }
230                 else {
231                         $num_checksums++;
232                         if (lc($sum) eq $digest) { $rsp .= "OK\n" }
233                         else { $rsp .= "FAILED\n"; $err = 1; $match_errs++ }
234                 }
235                 print $rsp unless $status;
236         }
237         close($fh);
238         unless ($status) {
239                 warn("shasum: WARNING: $read_errs of $num_files listed " .
240                         "files could not be read\n") if $read_errs;
241                 warn("shasum: WARNING: $match_errs of $num_checksums " .
242                         "computed checksums did NOT match\n") if $match_errs;
243         }
244         exit($err);
245 }
246
247
248         # Compute and display SHA checksums of requested files
249
250 for my $arg (@ARGV) {
251         if (-d $arg) {
252                 warn "shasum: $arg: Is a directory\n";
253                 next;
254         }
255         my $digest = sumfile($arg) or next;
256         print "$digest $modesym$arg\n";
257 }