The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/local/bin/perl

#
# An implementation of the 'cksum' utility in Perl.  Written for the Perl
# Power Tools (PPT) project by Theo Van Dinter (felicity@kluge.net).
#
# $Id: sum,v 1.2 2004/08/05 14:17:44 cwest Exp $
#

use integer;
use Getopt::Std;
use strict;
use vars qw($opt_h $opt_o);

&help unless getopts('ho:');
&help if ( $opt_h );

# default to bsd unless spec.
$opt_o = 1 unless ( $0 =~ /cksum$/ || defined($opt_o) );

@ARGV = ( "-" ) unless ( @ARGV ); # STDIN if no files specified.

# returns 1 on file read error, 0 if all ok.
my($exitval) = 0; # return value

foreach (@ARGV) {
	open(IN,$_) || die "Can't open file for reading:$!";
	my($rval,$crc,$len)=
		($opt_o==0)?&crc32(\*IN):($opt_o==1)?&sum1(\*IN):&sum2(\*IN);
	unless ( defined($rval) && ($rval == 0) ) {
		warn "$0: $_: $!\n";
		$exitval = 1;
		next;
	}

	# Display output information
	printf "%lu %lu%s\n",$crc,$len,($_ eq "-")?"":" $_";
	close(IN);
}

exit $exitval;

sub sum1 {
	my($fh) = shift;
	my($crc) = my($len) = 0;
	my($buf,$num,$i);
	my($buflen) = 4096; # buffer is "4k", you can up it if you want...

	while($num = sysread $fh, $buf, $buflen) {
		for($len = ($len+$num), $i = 0; $i<$num; $i++) {
			$crc |= 0x10000 if ( $crc & 1 ); # get ready for rotating the 1 below
			$crc = (($crc>>1)+ord(substr $buf, $i, 1)) & 0xffff; # keep to 16-bit
		}
	}
	
	return $num,$crc,($len+1023)/1024; # round # of blocks up ...
}

sub sum2 {
	my($fh) = shift;
	my($crc) = my($len) = 0;
	my($buf,$num,$i);
	my($buflen) = 4096; # buffer is "4k", you can up it if you want...

	while($num = sysread $fh, $buf, $buflen) {
		$len += $num;
		$crc += unpack("%32C*", $buf);
	}
	
	# crc = s (total of bytes)
	$crc = ($crc & 0xffff) + ($crc & 0xffffffff) / 0x10000; # r
	$crc = ($crc & 0xffff) + ($crc / 0x10000); # cksum

	return $num,$crc,($len+511)/512; # round # of blocks up ...
}

# does a bunch of ands to keep the answers within 32-bits
sub crc32 {
	my($fh) = shift;
	my($crc) = my($len) = 0;
	my($buf,$num,$i);
	my($buflen) = 4096; # buffer is "4k", you can up it if you want...

	# crctable/crc32 alg converted from openbsd's cksum program ...
	my(@crctable) = (
		0x0,
		0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
		0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
		0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
		0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
		0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
		0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
		0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
		0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
		0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
		0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
		0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
		0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
		0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
		0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
		0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
		0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
		0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
		0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
		0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
		0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
		0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
		0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
		0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
		0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
		0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
		0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
		0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
		0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
		0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
		0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
		0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
		0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
		0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
		0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
		0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
		0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
		0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
		0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
		0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
		0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
		0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
		0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
		0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
		0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
		0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
		0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
		0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
		0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
		0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
		0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
		0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
	);

	while($num = sysread $fh, $buf, $buflen) {
		for($len = ($len+$num)&0xffffffff, $i = 0; $i<$num; $i++) {
			$crc = ($crc << 8 ^ $crctable[$crc >> 24 ^ ord(substr $buf, $i, 1)])
					& 0xffffffff;
		}
	}
	
	my($rlen) = $len; # for reporting ...
	for(;$len!=0;$len>>=8) { # MSB first
		$crc = (($crc << 8) ^ $crctable[($crc >> 24) ^ ($len&0xff)])
				& 0xffffffff;
	}
	return $num,~$crc,$rlen;
}

sub help {
	print "
usage: $0 [-o 0|1|2] [-h] [file ...]

-o	Specifies algorithm to use ... 0 is CRC32 (default), 1 is BSD
	Algorithm 1, 2 is SYSV Algorithm 2.
-h	Help!  (this display)

Note: If this program is launched with a name other than 'cksum', the default
      of BSD Algorithm 1 is used unless otherwise specified via switch.

";

	exit 0;
}

=head1 NAME

sum - display file checksums and block counts

=head1 SYNOPSIS

B<sum>
[ B<-o> I<0|1|2> ]
[ B<-h> ]
[ I<filename ...> ]

=head1 DESCRIPTION

sum outputs three space seperated values:  file CRC, file size, and
file name.  Can be used to find errors in transmitted files.  You should
not use sum for security checks as they are easily fooled.  Look into
md5sum for something a bit more secure.  If no file names are specified,
stdin is used and no file name will displayed in the output.

=head1 OPTIONS AND ARGUMENTS

=item I<-h>	Display the usage help message.

=item I<-o>	Specify the output type for file CRC and size.
C<0>--CRC is computed using the CRC 32 algorithm, the default unless
otherwise specified.  Output size is in bytes.	C<1>--CRC is computed
using BSD Historic Algorithm 1 (16-bit checksum with right rotation
between byte addition).  Output size is number of 1024 byte blocks.
C<2>--CRC is computed using SYSV Historic Algorithm 2 (32-bit checksum).
Output size is number of 512 byte blocks.

=head1 NOTES

sum returns 0 on success or 1 if an error occurred.

The program checks the name in which it was called.  If it is anything
except "cksum", the output will default to BSD Historic Algorithm 1
unless otherwise specified.  Otherwise, the default is CRC32 mode.

Algorithms 1 and 2 will round up to the next block count for partial blocks.

CRC 32 algorithm ported directly from OpenBSD cksum C source code.

=head1 HISTORY

Perl version rewritten for the Perl Power Tools project from the
description of the cksum program in OpenBSD.

=head1 AUTHOR

Theo Van Dinter (felicity@kluge.net)

=head1 SEE ALSO

md5sum(1)