#!/usr/bin/perl -wT #-*-perl-*- # $rcs = ' $Id: md5sum,v 1.5 2001/04/12 07:12:12 idnopheq Exp idnopheq $ ' ; use strict; use FileHandle; use Digest::MD5; use Getopt::Mixed 1.006, "nextOption"; use vars qw ( %option ); my ($VERSION) = '$Revision: 1.5 $' =~ /([.\d]+)/; my $warnings = 0; # Print a usage message on a unknown option. # Requires abigail's patch to Getopt::Std of 25 Feb 1999. $SIG {__WARN__} = sub { if (substr ($_ [0], 0, 14) eq "Unknown option") { die "Usage" }; require File::Basename; $0 = File::Basename::basename ($0); $warnings = 1; warn "$0: @_"; }; $SIG {__DIE__} = sub { require File::Basename; $0 = File::Basename::basename ($0); if (substr ($_ [0], 0, 7) eq "Version") { die <binary", # -b an alias for --binary "bsd", # FreeBSD, maybe OpenBSD?, format "B>bsd", # -B an alias for --bsd "check", # check manifest file "c>check", # -c an alias for --check "help", # more detailed help "?>help", # -? an alias for --help "h>help", # -h an alias for --help "p", # does nothing, included for FreeBSD "quiet", # only the MD5 sum is printed out "q>quiet" , # -q an alias for --quiet "reverse", # Reverses the format of the output # like FSF but no binary * "r>reverse", # -r an alias for --reverse "status", # return 0 on ok, non-0 on badness # only useful w/ -c or --check "string=s", # Print a checksum of a given string "s>string", # -s an alias for --string "text", # text mode (default) "t>text", # -t an alias for --text "v", # does nothing, included for Win32 "version", # print the version number and exit "V>version", # -V an alias for --version "warn", # show # of ok and bad checksums # only useful w/ -c or --check "w>warn", # -w an alias for --warn ); # process the options and gently place them in a hash for easy access while ( my ( $option, $value, $asEntered ) = nextOption () ) { $option{$option} = $value || 1; } # cleanup options Getopt::Mixed::cleanup(); # *** BEGGING FOR SWITCH CONSTRUCT die "Version" if $option{'version'}; die "Help" if $option{'help'}; # if the 'string' option was presented, $option{'string'} will hold that # string, so hash it & print & outta here if ( $option{'string'} ) { my $inMD5 = Digest::MD5->new; $inMD5->add ( $option{'string'} ); print outputHash( $option{'string'}, $inMD5->hexdigest, ' ' ); exit; } # if we are checking file from a manifest file, the do it and bye! if ( $option{'check'} ) { checkManifest ( $ARGV[0] ); exit; } # figure out if we're working binary via OS and a 'text' override $option{'binary'} ||= $^O =~ /MSWin32/; undef $option{'binary'} if $option{'text'}; $ARGV[0] ||= "-"; # here endeth the begging *** # MAIN, if you will # parse each remaining option and treat it as a file to hash foreach my $file ( @ARGV ) { chomp $file; my ( $md5Digest, $binary ) = MD5 ( $file ); print &outputHash ( $file, $md5Digest, $binary ); } sub MD5 { my $file = shift; my $prefix = ' '; my $inFile = new FileHandle; my $inMD5 = Digest::MD5->new; unless (-e $file) { warn "$file: No such file or directory\n"; return; } unless ( $inFile->open ( "<$file" ) ) { warn "Could not open $file:$!\n"; return; } binmode ( $inFile ) && ( $prefix = '*' ) if $option{'binary'}; $inMD5->addfile ( $inFile ); $inFile->close; # being tidy return $inMD5->hexdigest, $prefix; } sub checkManifest { my $file = shift; my $inFile = new FileHandle; unless (-e $file) { warn "$file: No such file or directory\n"; return; } $inFile->open ( "<$file" ) or die "Could not open $file:$!\n"; my $ok = my $failed = 0; while ( <$inFile> ) { next if /^#/; # skip comments # our manifest file ~should be~ in a fixed format if sane my ( $md5Hash, $binary, $fileName ) = unpack "a32 a2 A*", $_; warn "$fileName: No such file or directory\n" && next unless -e $fileName; my $karma = verifyHash ( $md5Hash, $fileName ); KARMA: { unless ( $karma && -e $fileName ) { warn "$fileName: No such file or directory\n"; $failed++; return; return; } unless ( $karma ) { warn "$fileName: Improperly formatted MD5 checksum line\n"; $failed++; next; return; } bless $inFile; } my $md5 = ( MD5 ( $fileName ) )[0] or return; $option{'binary'} = ( $binary =~ /\*/ ) || undef; if ( $md5Hash eq $md5 ) { print $fileName , ": OK\n" unless $option{'status'}; $ok++; } else { print $fileName , ": FAILED\n" unless $option{'status'}; $failed++; } } if ( $option{'status'} ) { if ( $failed > 0 ) { return 1; }; return 0; } $= = $ok+$failed; warn "WARNING: $failed of ", $=, " computed checksums did NOT match\n" if ( $failed > 0 && $option{'warn'} ); } sub verifyHash { my ( $hash, $file ) = @_; if ( length $hash != 32 ) { return } elsif ( ! -e $file ) { return 2 } return 1; } sub onError { require File::Basename; return File::Basename::basename ($0); } sub outputHash { my ( $file, $md5Hash, $bin ) = @_; return unless $md5Hash; if ( ( defined $option{'bsd'} ) + ( defined $option{'quiet'} ) + ( defined $option{'reverse'} ) > 1 ) { warn "Too many output options defined. Setting to default (FSF) style.\n"; undef $option{'bsd'}; undef $option{'quiet'}; undef $option{'reverse'}; } OUTPUT: { $option{'bsd'} && do { return onError() , " ($file) = $md5Hash\n"; last OUTPUT; }; $option{'quiet'} && do { return "$md5Hash\n"; last OUTPUT; }; $option{'reverse'} && do { return "$md5Hash $file\n"; last OUTPUT; }; return "$md5Hash $bin$file\n"; } } __DATA__ =pod =head1 NAME md5sum - Print or check message-digests =head2 SYNOPSIS md5sum [option]... [file]... md5sum [option]... --check [file] =head2 DESCRIPTION md5sum computes a 128-bit checksum (or fingerprint or message-digest) for each specified file. If a file is specified as `-' or if no files are given md5sum computes the checksum for the standard input. md5sum can also determine whether a file and checksum are consistent. Synopses: For each file, `md5sum' outputs the MD5 checksum, a flag indicating a binary or text input file, and the filename. If file is omitted or specified as `-', standard input is read. =head2 OPTIONS The program accepts the following options. =item -b --binary Treat all input files as binary. This option has no effect on Unix systems, since they don't distinguish between binary and text files. This option is useful on systems that have different internal and external character representations. On MS-DOS and MS-Windows, this is the default. =item -B --bsd Provide FreeBSD default output Like the md5 command of the BSD-derrivitives. This output will not work with the check feature. =item -c --check Read filenames and checksum information from the single file (or from stdin if no file was specified) and report whether each named file and the corresponding checksum data are consistent. The input to this mode of md5sum is usually the output of a prior, checksum-generating run of `md5sum'. Each valid line of input consists of an MD5 checksum, a binary/text flag, and then a filename. Binary files are marked with `*', text with ` '. For each such line, md5sum reads the named file and computes its MD5 checksum. Then, if the computed message digest does not match the one on the line with the filename, the file is noted as having failed the test. Otherwise, the file passes the test. By default, for each valid line, one line is written to standard output indicating whether the named file passed the test. After all checks have been performed, if there were any failures, a warning is issued to standard error. Use the `--status' option to inhibit that output. If any listed file cannot be opened or read, if any valid line has an MD5 checksum inconsistent with the associated file, or if no valid line is found, md5sum exits with nonzero status. Otherwise, it exits successfully. =item -p Echo stdin to stdout and appends the MD5 sum to stdout (Default w/o options, so ignorred). For *BSD boxen. =item -q --quiet Quiet mode Only the MD5 sum is printed out. Overrides the -r option. =item -r --reverse Reverses the format of the output This helps with visual diffs, if you're on a *BSD box. Simillar to default output, but w/o the binary star '*' prepending binary files. This may be important if you're on a Win32 boxen. =item --status This option is useful only when verifying checksums. When verifying checksums, don't generate the default one-line-per-file diagnostic and don't output the warning summarizing any failures. Failures to open or read a file still evoke individual diagnostics to standard error. If all listed files are readable and are consistent with the associated MD5 checksums, exit successfully. Otherwise exit with a status code indicating there was a failure. =item -s --string Print the MD5 checksum of the string This option overrides most options, and requires you to provide text as an argument to the option. Based on your shell, you will likely want to single quote ' the text on both ends. =item -t --text Treat all input files as text files This is the reverse of `--binary'. =item -v Verbose output (Default w/o options, so ignorred). For MS-DOS-derrived boxen. =item -w --warn This option is useful only when verifying checksums. When verifying checksums, warn about improperly formatted MD5 checksum lines. This option is useful only if all but a few lines in the checked input are valid. =head2 SEE ALSO I dunno? Some man pages, like md5sum and md5? =head2 ACKNOWLEDGEMENTS RSA Data Security probably think they get some credit. I'd like to thank the Academy, all those people who I love, and of course the Perl Monks, especially abigail. =head2 LICENSE # Copyright (c) 2001 # Dexter Coffin. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY DEXTER COFFIN AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL DEXTER COFFIN OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # =cut