#!perl -w
###############################################################################
use strict;
use Digest::file qw(digest_file_hex);
use Compress::Raw::Zlib;
use FileHandle;

my %alg =
( "MD2"      => { on=>0, syn=>[] }
, "MD4"      => { on=>0, syn=>[] }
, "MD5"      => { on=>1, syn=>['128', '5'] }
, "SHA-1"    => { on=>0, syn=>['160', '1','sha1','sha', 'sha160','sha-160'] }
, "SHA-224"  => { on=>0, syn=>['224', 'sha224'] }
, "SHA-256"  => { on=>0, syn=>['256', 'sha256'] }
, "SHA-384"  => { on=>0, syn=>['384', 'sha384'] }
, "SHA-512"  => { on=>0, syn=>['512', 'sha512'] }
, "CRC-16"   => { on=>0, syn=>['crc16', '16', ] }
, "CRC-32"   => { on=>0, syn=>['crc32', '32', 'crc'] }
, "CRC-CCITT"=> { on=>0, syn=>['ccitt', 'crcccitt'] }
# "CRC32"    => { on=>0, syn=>['crc', '32'],        crc32=>\&crc32,   crc0=>0 }
, "Adler32"  => { on=>0, syn=>['adler','adler-32'], crc32=>\&adler32, crc0=>0 }
);

my $usage = '
usage:
  digest.pl [option ...] file [[option ...] file ...]

option:
  {+|-|:}algorithm
  -debug
  -quiet

algorithm:
 ';

my $VERBOSE = 2;
my ($nf, $a, $k, $act, $alg, $t, $c, $h) = (0);

for $k (sort keys %alg)
{
   $usage .= " $k";
   $usage .= "[=default]" if $alg{$k}->{on};
}
$usage .= "\n";

###############################################################################
sub crc_file($$;$)
{
   my ($file, $func, $crc) = @_;
   $crc = 0 unless defined $crc;

   my ($fh, $buff, $nr) = FileHandle->new();
   $fh->open( $file, 'r' ) || die "Can't open file: $file -- $!";
   binmode($fh);

   while ($nr = $fh->read($buff, 64*1024)) { $crc = &$func($buff, $crc); }

   $fh->close();
   return $crc;
}

###############################################################################
for $a (@ARGV)
{
   if ( $a =~ m!^([+:-])([\w-]+)$!io )
   {
      my ( $act, $alg, $k, $found) = ($1, lc($2), '', 0);

      if ( $alg =~ m!^(d|debug|dbg|v|verb|verbose)$!io ) { ++$VERBOSE; next; }
      if ( $alg =~ m!^(q|quiet)$!io )                    { --$VERBOSE; next; }

      for $k (keys %alg)
      {
         for $t (@{$alg{$k}->{syn}}) {
            if ( $alg eq lc($t) ) { $alg = $k; last; }
         }
         if ( lc($alg) eq lc($k) ) {
            $alg{$k}->{on} = $act ne '-';
            ++$found;
            $alg = $k;
         }
         else {
            $alg{$k}->{on} = 0 if ( ':' eq $act );
         }
      }
      die $usage unless $found;
   }

   elsif ( -f $a )
   {
      print "$a:\n" if ( $VERBOSE >= 2);
      for $k (sort keys %alg)
      {
         next unless $alg{$k}->{on};

         if ( ($c = $alg{$k}->{crc32}) ) {
            $h = sprintf("%08x", crc_file($a, $c, $alg{$k}->{crc0}));
         }
         elsif ( ($c = $alg{$k}->{crc16}) ) {
            $h = sprintf("%04x", crc_file($a, $c, $alg{$k}->{crc0}));
         }
         else {
            $h = digest_file_hex($a, $k);
         }

         if ( $VERBOSE > 0 ) {
            if ( $VERBOSE < 2) { print "$h\n"; } else { print "  $k = $h\n"; }
         }
         ++$nf;
      }
   }

   else { die $usage; }
}

die $usage unless $nf > 0;

###############################################################################
1;
