#!/usr/local/bin/ruby # $Id: digest,v 1.3 2002/05/20 00:44:56 sdalu Exp sdalu $ # # Copyrigth (C) 2002 Stephane D'Alu # Code is under GPL v2 (see http://www.gnu.org/licenses/gpl.txt) # # WWW: http://www.sdalu.com/software/ # RCS_ID = %q$Id: digest,v 1.3 2002/05/20 00:44:56 sdalu Exp sdalu $ RCS_REVISION = RCS_ID.split[2] PROGNAME = File.basename($0) require 'getoptlong' require 'digest/md5' require 'digest/sha1' require 'digest/rmd160' # Exit values EXIT_OK = 0 EXIT_USAGE = -1 EXIT_BADCKSUM = 1 EXIT_MISSING = 2 EXIT_ERROR = 3 EXIT_ABORTED = 4 # Constants BLOCKSIZE = 4096 # Arguments default value quiet = false veryquiet = false scheme = "MD5" check = false files = nil reverse = false # Parse command line opts = GetoptLong.new( [ "--check", "-c", GetoptLong::NO_ARGUMENT ], [ "--reverse", "-r", GetoptLong::NO_ARGUMENT ], [ "--scheme", "-s", GetoptLong::REQUIRED_ARGUMENT], [ "--quiet", "-q", GetoptLong::NO_ARGUMENT ], [ "--veryquiet","-Q", GetoptLong::NO_ARGUMENT ], [ "--help", "-h", GetoptLong::NO_ARGUMENT ], [ "--version", "-v", GetoptLong::NO_ARGUMENT ] ) opts.quiet = true begin opts.each do |opt, arg| case opt when "--check" then check = true when "--reverse" then reverse = true when "--scheme" then scheme = arg when "--quiet" then quiet = true when "--veryquiet" then veryquiet = true when "--help" then raise when "--version" puts "#{PROGNAME}: RCS version #{RCS_REVISION}" exit EXIT_OK end end raise if check && ARGV.length > 1 files = ARGV rescue $stderr.print </SHA1/RMD160) -r, --reverse Reverse output order -q, --quiet Quiet mode, don't print good file status -Q, --veryquiet Very Quiet mode, doesn't print visual candy -h, --help Show this message -v, --version Display RCS version EOT exit EXIT_USAGE end # Prepare for abortion cleanup trap "SIGINT", proc { exit EXIT_ABORTED } # def get_hashing_scheme(scheme) case scheme when "MD5" then Digest::MD5 when "SHA1" then Digest::SHA1 when "RMD160" then Digest::RMD160 else raise ArgumentError, "Unknown hash scheme '#{scheme}'" end end if check then badcksum, missing, lineno = 0, 0, 0 # Select input begin io = files.empty? ? $stdin : File::open(files[0]) rescue Errno::ENOENT => e $stderr.puts "#{PROGNAME}: file #{files[0]} doesn't exist." exit EXIT_ERROR end # Check hash values io.each { |line| lineno += 1 begin # Parse line case line when /^([A-Z][A-Z0-9]*) \((.*)\) = ([a-f0-9]+)$/ scheme, hash, filename = $1, $3, $2 when /^([a-f0-9]{32}) (.*)$/ scheme, hash, filename = "MD5", $1, $2 else raise RuntimeError, "Unable to parse content" end # Identify hashing scheme h = get_hashing_scheme(scheme)::new # Hash file missing += 1 File.open(filename) { |io| while data = io.read(BLOCKSIZE) ; h << data ; end } missing -= 1 # Check if h == hash then puts "OK: #{filename}" unless veryquiet || quiet else puts "FAILED: #{filename}" unless veryquiet badcksum += 1 end rescue RuntimeError, Errno::ENOENT => e $stderr.puts "ERROR: #{e} (line #{lineno})" unless veryquiet end } # Print final status if badcksum != 0 then msg = "==> #{badcksum} bad file(s)" msg += " / #{missing} missing file(s)" if missing != 0 puts msg exit EXIT_BADCKSUM elsif missing != 0 then puts "==> #{missing} missing file(s)" exit EXIT_MISSING else exit EXIT_OK end else # Check hashing scheme begin scheme.upcase! hashing_scheme = get_hashing_scheme(scheme) rescue ArgumentError => e $stderr.puts "#{PROGNAME}: #{e}" exit EXIT_ERROR end # STDIN or file list if files.empty? then # Generate hash value from STDIN h = hashing_scheme::new while data = $stdin.read(BLOCKSIZE) ; h << data ; end puts h else # Generate hash value from supplied file list files.each { |file| h = hashing_scheme::new begin File.open(file) { |io| while data = io.read(BLOCKSIZE) ; h << data ; end puts reverse ? "#{h} #{file}" \ : "#{scheme} (#{file}) = #{h}" } rescue Errno::ENOENT => e $stderr.puts "ERROR: #{e}" end } end # Leave exit EXIT_OK end