Commit | Line | Data |
6bc89f92 |
1 | #!perl -w |
2 | |
3 | # shasum: filter for computing SHA digests (analogous to md5sum) |
4 | # |
77d2a621 |
5 | # Copyright (C) 2003-2006 Mark Shelor, All Rights Reserved |
6bc89f92 |
6 | # |
dcbcf62d |
7 | # Version: 5.37 |
8 | # Mon May 8 04:30:09 MST 2006 |
6bc89f92 |
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 |
128cbdba |
24 | -p, --portable read files in portable mode |
25 | produces same digest on Windows/Unix/MacOS |
6bc89f92 |
26 | -t, --text read files in text mode (default) |
27 | |
28 | The following two options are useful only when verifying checksums: |
29 | |
128cbdba |
30 | -s, --status don't output anything, status code shows success |
6bc89f92 |
31 | -w, --warn warn about improperly formatted SHA checksum lines |
32 | |
128cbdba |
33 | -h, --help display this help and exit |
34 | -v, --version output version information and exit |
6bc89f92 |
35 | |
128cbdba |
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. |
6bc89f92 |
40 | |
41 | =head1 AUTHOR |
42 | |
77d2a621 |
43 | Copyright (c) 2003-2006 Mark Shelor <mshelor@cpan.org>. |
6bc89f92 |
44 | |
45 | =head1 SEE ALSO |
46 | |
128cbdba |
47 | shasum is implemented using the Perl module L<Digest::SHA> or |
6bc89f92 |
48 | L<Digest::SHA::PurePerl>. |
49 | |
50 | =cut |
51 | |
52 | use strict; |
53 | use Getopt::Long; |
54 | |
dcbcf62d |
55 | my $VERSION = "5.37"; |
6bc89f92 |
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. |
128cbdba |
74 | # Include an "-a" option for algorithm selection, |
75 | # and a "-p" option for portable digest computation. |
6bc89f92 |
76 | |
77 | sub usage { |
128cbdba |
78 | my($err, $msg) = @_; |
6bc89f92 |
79 | |
128cbdba |
80 | $msg = "" unless defined $msg; |
81 | if ($err) { |
82 | print STDERR "$msg", "Type shasum -h for help\n"; |
83 | exit($err); |
84 | } |
85 | print STDOUT <<'END_OF_USAGE'; |
6bc89f92 |
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 |
128cbdba |
94 | -p, --portable read files in portable mode |
95 | produces same digest on Windows/Unix/MacOS |
6bc89f92 |
96 | -t, --text read files in text mode (default) |
97 | |
98 | The following two options are useful only when verifying checksums: |
128cbdba |
99 | -s, --status don't output anything, status code shows success |
6bc89f92 |
100 | -w, --warn warn about improperly formatted SHA checksum lines |
101 | |
128cbdba |
102 | -h, --help display this help and exit |
103 | -v, --version output version information and exit |
6bc89f92 |
104 | |
128cbdba |
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. |
6bc89f92 |
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); |
128cbdba |
119 | my ($portable); |
6bc89f92 |
120 | |
128cbdba |
121 | Getopt::Long::Configure ("bundling"); |
6bc89f92 |
122 | GetOptions( |
128cbdba |
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, ""); |
6bc89f92 |
129 | |
130 | |
131 | # Deal with help requests and incorrect uses |
132 | |
128cbdba |
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, "warn option used only when verifying checksums\n") |
138 | if $warn && !$check; |
139 | usage(1, "status option used only when verifying checksums\n") |
140 | if $status && !$check; |
6bc89f92 |
141 | |
142 | |
143 | # Default to SHA-1 unless overriden by command line option |
144 | |
145 | $alg = 1 unless $alg; |
128cbdba |
146 | grep { $_ == $alg } (1, 224, 256, 384, 512) |
147 | or usage(1, "unrecognized algorithm\n"); |
6bc89f92 |
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 |
128cbdba |
160 | # explicitly overriden by command line "text" or |
161 | # "portable" options. |
6bc89f92 |
162 | |
163 | my $isDOSish = ($^O =~ /^(MSWin\d\d|os2|dos|mint|cygwin)$/); |
128cbdba |
164 | if ($isDOSish) { $binary = 1 unless $text || $portable } |
165 | |
166 | my $mode = $binary ? '*' : ($portable ? '?' : ' '); |
6bc89f92 |
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 { |
dcbcf62d |
177 | my $file = shift; |
6bc89f92 |
178 | |
dcbcf62d |
179 | local *F; |
180 | return unless open(F, "<$file"); |
181 | binmode(F) if $binary || $portable; |
128cbdba |
182 | |
dcbcf62d |
183 | my $digest = $module->new($alg); |
128cbdba |
184 | if ($portable && -T $file) { |
dcbcf62d |
185 | local $/ = \4096; |
186 | while (defined (my $rec = <F>)) { |
187 | while (substr($rec, -1) eq "\015") { |
188 | defined (my $extra = <F>) or last; |
189 | $rec .= $extra; |
190 | } |
191 | $rec =~ s/\015+\012/\012/g; |
192 | $rec =~ s/\015/\012/g; |
193 | $digest->add($rec); |
194 | } |
128cbdba |
195 | } |
dcbcf62d |
196 | else { $digest->addfile(*F) } |
197 | close(F); |
128cbdba |
198 | |
dcbcf62d |
199 | $digest->hexdigest; |
6bc89f92 |
200 | } |
201 | |
202 | |
203 | # %len2alg: maps hex digest length to SHA algorithm |
204 | |
205 | my %len2alg = (40 => 1, 56 => 224, 64 => 256, 96 => 384, 128 => 512); |
206 | |
207 | |
208 | # Verify checksums if requested |
209 | |
210 | if ($check) { |
211 | my $checkfile = shift(@ARGV); |
212 | my $err = 0; |
213 | my ($fh, $sum, $fname, $rsp); |
214 | |
215 | die "shasum: $checkfile: No such file or directory\n" |
216 | unless open($fh, "<$checkfile"); |
217 | while (<$fh>) { |
218 | s/\s+$//; |
128cbdba |
219 | ($sum, $mode, $fname) = /^(\S+) (.)(.*)$/; |
dcbcf62d |
220 | unless (-e $fname) { |
221 | warn "shasum: $fname: No such file or directory\n"; |
222 | next; |
223 | } |
128cbdba |
224 | ($binary, $portable, $text) = |
225 | map { $_ eq $mode } ('*', '?', ' '); |
6bc89f92 |
226 | unless ($alg = $len2alg{length($sum)}) { |
dcbcf62d |
227 | warn("shasum: $checkfile: $.: improperly " . |
228 | "formatted SHA checksum line\n") if $warn; |
6bc89f92 |
229 | next; |
230 | } |
231 | $rsp = "$fname: "; |
dcbcf62d |
232 | unless (my $digest = sumfile($fname)) { |
233 | $rsp .= "FAILED open or read\n"; |
234 | $err = 1; |
235 | } |
236 | else { |
237 | if (lc($sum) eq $digest) { $rsp .= "OK\n" } |
238 | else { $rsp .= "FAILED\n"; $err = 1 } |
239 | } |
6bc89f92 |
240 | print $rsp unless $status; |
241 | } |
242 | close($fh); |
243 | exit($err); |
244 | } |
245 | |
246 | |
247 | # Compute and display SHA checksums of requested files |
248 | |
dcbcf62d |
249 | for my $arg (@ARGV) { |
250 | if (-d $arg) { |
251 | print STDERR "shasum: $arg: Is a directory\n"; |
6bc89f92 |
252 | next; |
253 | } |
dcbcf62d |
254 | next unless my $digest = sumfile($arg); |
255 | print "$digest $mode", "$arg\n"; |
6bc89f92 |
256 | } |