#!/usr/bin/perl
# create checksums for scripts, per line
# verify checksums against checksum file
# Jerry Stratton, AstoundingScripts.com

use Digest::MD5 qw(md5_base64);

$binDir = $ENV{'HOME'} . '/bin';
$checksumDir = $ENV{'HOME'} . '/Documents/scripts/checksums';
$tab = "\t";

$scriptName = shift;
if ($scriptName eq '--create') {
	$createChecksums = 1;
	`mkdir -p $checksumDir`;
	$scriptName = shift;
}

die("Scriptname must be provided.") if !$scriptName;

$scriptPath = "$binDir/$scriptName";
$checksumPath = "$checksumDir/$scriptName.txt";

die "No script $scriptName in $binDir.\n" if !-f $scriptPath;
die "No checksum file for $scriptName in $checksumDir.\n" if !$createChecksums && !-f $checksumPath;

@ARGV = ($scriptPath);

while (<>) {
	$line++;
	chomp;
	s/^(($tab)*)//;
	my $indentation = length($1)/length($tab);
	my $lineSize = length();
	my $checksum = 0;
	for $character (split //) {
		$checksum += ord($character);
	}
	my $hash = md5_base64($_);

	$binLines[$#binLines+1] = "$line\t$indentation\t$lineSize\t$checksum\t$hash";
}

if ($createChecksums) {
	die("Unable to open $checksumPath for writing: $!") unless open($checksumHandle, '>', $checksumPath);
	print $checksumHandle join("\n", @binLines);
} else {
	die("Unable to open $checksumPath for reading: $!") unless open($checksumHandle, '<', $checksumPath);
	while (<$checksumHandle>) {
		chomp;
		next if /^$/;
		($line, $indentation, $lineSize, $checksum, $hash) = split("\t");
		($binLine, $binDentation, $binSize, $binSum, $binHash) = split("\t", $binLines[$line-1]);

		die "source file $scriptPath ends too early at line $line\n" if $line > $#binLines+1;
		die "line $line: line numbers do not match ($binLine)\n" if $line != $binLine;

		$indentError = $binDentation - $indentation;
		$sizeError = $binSize - $lineSize;
		$sumError = $binSum - $checksum;
		print "line $line has ", abs($indentError), " too few tabs\n" if $indentError < 0;
		print "line $line has $indentError too many tabs\n" if $indentError > 0;
		print "line $line has ", abs($sizeError), " too few characters\n" if $sizeError < 0;
		print "line $line has $sizeError too many characters\n" if $sizeError > 0;
		print "line $line’s character sum is low by ", abs($sumError) if $sumError < 0;
		print "line $line’s character sum is high by $sumError" if $sumError > 0;
		print ' (', chr(abs($sumError)), ')' if $sumError && $sizeError == 1;
		print "\n" if $sumError;
		print "line $line has a typo (digests unequal)\n" if !$sizeError && !$sumError && ($hash ne $binHash);
	}
}
close $checksumHandle;
