3 #==============================================================================
5 # File ID: ac639b66-f942-11dd-8b88-0001805bf4b1
7 # Generates smsum hashes
10 # ©opyleft 2008– Øyvind A. Holm <sunny@sunbase.org>
11 # License: GNU General Public License version 2 or later, see end of file for
13 #==============================================================================
34 $progname =~ s/^.*\/(.*?)$/$1/;
35 our $VERSION = '0.1.2';
37 Getopt
::Long
::Configure
('bundling');
40 'help|h' => \
$Opt{'help'},
41 'quiet|q+' => \
$Opt{'quiet'},
42 'verbose|v+' => \
$Opt{'verbose'},
43 'version' => \
$Opt{'version'},
44 'with-mtime|m' => \
$Opt{'with-mtime'},
46 ) || die("$progname: Option error. Use -h for help.\n");
48 $Opt{'verbose'} -= $Opt{'quiet'};
49 $Opt{'help'} && usage
(0);
50 if ($Opt{'version'}) {
63 $result = process_file
($_);
64 if (defined($result)) {
71 $result = process_file
("-");
72 if (defined($result)) {
86 msg
(3, "process_file('$Filename')");
87 my $use_stdin = ($Filename eq "-") ?
1 : 0;
89 my $displayed_filename = $Filename;
90 my $tmpdir = (defined($ENV{'TMPDIR'}) && length($ENV{'TMPDIR'}))
91 ?
$ENV{'TMPDIR'} : "/tmp";
92 my $tmpfile = "$tmpdir/$progname." . time . ".$$.tmp";
94 open(my $tmpfp, '>', $tmpfile) or
95 die("$progname: $tmpfile: Cannot create tempfile: $!\n");
96 unless (print($tmpfp join('', <STDIN
>))) {
100 die("$progname: $tmpfile: Cannot write to tempfile: $errmsg\n");
103 $Filename = $tmpfile;
105 if (@stat_array = stat($Filename)) {
106 my ($Dev, $Inode, $Mode, $Nlinks, $Uid, $Gid, $Rdev, $Size,
107 $Atime, $Mtime, $Ctime, $Blksize, $Blocks) = @stat_array;
108 if ($use_stdin || -f
$Filename) {
110 if ($use_stdin || open(FP
, "<", $Filename)) {
111 msg
(2, sprintf("Reading %s...", safe_tab
($Filename)));
112 $Sum{'sha1'} = sha1sum
($Filename);
113 $Sum{'md5'} = md5sum
($Filename);
118 $use_stdin && !$Opt{'with-mtime'}
120 : "\t" . safe_tab
($displayed_filename) . (
122 ?
"\t" . sec_to_string
($Mtime)
128 warn("$progname: $Filename: Cannot read file\n");
132 msg
(1, "$Filename: Ignoring non-file");
135 warn("$progname: $Filename: Cannot read file status\n");
144 my $sha1 = Digest
::SHA
->new(1);
146 open(FP
, "<", "$file") or return undef;
147 while (my $Curr = <FP
>) {
150 return $sha1->hexdigest;
155 my $md5 = Digest
::MD5
->new;
157 open(FP
, "<", "$file") or return undef;
158 while (my $Curr = <FP
>) {
161 return $md5->hexdigest;
166 $Str =~ s/\\/\\\\/gs;
174 # Convert seconds since 1970 to "yyyy-mm-ddThh:mm:ssZ"
175 my ($Seconds) = shift;
177 my @TA = gmtime($Seconds);
178 my($DateString) = sprintf("%04u-%02u-%02uT%02u:%02u:%02uZ",
179 $TA[5]+1900, $TA[4]+1, $TA[3],
180 $TA[2], $TA[1], $TA[0]);
185 # Print program version
186 print("$progname $VERSION\n");
191 # Send the help message to stdout
194 if ($Opt{'verbose'}) {
200 Usage: $progname [options] [file [files [...]]]
202 The program is based on the same principle as md5sum(1) and sha1sum(1),
203 but combines the two hashes and also includes the file size:
205 [SHA1][-][MD5][-][SIZE][\\t][FILENAME][\\n]
207 or if the --with-mtime option is used:
209 [SHA1][-][MD5][-][SIZE][\\t][FILENAME][\\t][MTIME][\\n]
211 The reason for this approach, is that both hashing algoritms are well
212 known and widely used. Both algorithms are good enough for everyday
213 content verification, but at least the MD5 algorithm is vulnerable to
214 intentional collisions. Instead of inventing new algorithms which has to
215 earn trust over the years, combining the two well examined algorithms
216 and adding the size of the file will make a smsum hash collision much
219 If no filenames are specified on the command line, stdin is used.
221 Special characters in filenames are escaped this way:
223 Horizontal Tab (0x09): \\t
224 Line feed (0x0a): \\n
225 Carriage return (0x0d): \\r
226 Backslash ('\\', 0x5c): \\\\
233 Also include file modification time at the end of every line. The
234 date uses the UTC timezone and has the format
235 "yyyy-mm-ddThh:mm:ssZ". If stdin is read, the current time is used.
237 Be more quiet. Can be repeated to increase silence.
239 Increase level of verbosity. Can be repeated.
241 Print version information.
248 # Print a status message to stderr based on verbosity level
249 my ($verbose_level, $Txt) = @_;
251 if ($Opt{'verbose'} >= $verbose_level) {
252 print(STDERR
"$progname: $Txt\n");
259 # This program is free software; you can redistribute it and/or modify it under
260 # the terms of the GNU General Public License as published by the Free Software
261 # Foundation; either version 2 of the License, or (at your option) any later
264 # This program is distributed in the hope that it will be useful, but WITHOUT
265 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
266 # FOR A PARTICULAR PURPOSE.
267 # See the GNU General Public License for more details.
269 # You should have received a copy of the GNU General Public License along with
271 # If not, see L<http://www.gnu.org/licenses/>.
273 # vim: set ts=4 sw=4 sts=4 noet fo+=w tw=79 fenc=UTF-8 :