#!/usr/bin/perl

use bignum;       # support for numbers greater than 2^32 - 1
use Pod::Usage;   # error messages using internal POD
use Getopt::Long; # command line option parser

my $block_size = 512; # defaults to 512 byte
my $block_type = "?"; # defaults to "?" (non-tried block)

my $block_offset = '';
my $logfile = '';

GetOptions('block-size|b=i' => \$block_size,
           'block-type|l=s' => \$block_type,
           'help|h|?' => sub { pod2usage({-exitval => 1}) }) or pod2usage(2);

$block_offset = @ARGV[0];
shift(@ARGV); # remove block offset from list so that while <> loop further down sees only $logfile
$logfile = @ARGV[0];

if ($block_offset !~ m/^\d+$/)
{
	$msg = "Error: Block offset has to be an integer\n";
	pod2usage({-exitval => 2, -message => $msg, -verbose => 1});
}
if ($block_offset eq '')
{
	$msg = "Error: Block offset undefined\n";
	pod2usage({-exitval => 2, -message => $msg});
}
if ($logfile eq '')
{
	$msg = "Error: Log filename undefined\n";
	pod2usage({-exitval => 2, -message => $msg});
}
if (! -r $logfile)
{
	$msg = "Error: Log file $logfile cannot be opened for reading\n";
	pod2usage({-exitval => 2, -message => $msg});
}

$byte_offset = $block_offset * $block_size;

local $^I = '.bak'; # This enables in-place editing with backup named *.bak

while (<>)
{
	if ($_ =~ m|^0x([0-9A-F]{8,})  0x([0-9A-F]{8,})  ([?*/+-])$|)
	{
		$start = hex($1);
		$length = hex($2);
		$end = $start + $length;
		$type = $3;
		if (($start <= $byte_offset) and ($end > $byte_offset))
		{
			if ($start < $byte_offset)
			{
				printf("0x%08X  0x%08X  %s\n", $start, $byte_offset - $start, $type);
			}
			printf("0x%08X  0x%08X  %s\n", $byte_offset, $block_size, $block_type);
			$block_end = $byte_offset + $block_size;
			if ($end > $block_end)
			{
				printf("0x%08X  0x%08X  %s\n", $block_end, $end - $block_end, $type);
			}
		}
		else
		{
			print $_;
		}
	}
	else
	{
		print $_;
	}
}

__END__

=pod

=head1 NAME

ddrlogmod.pl - modifies GNU ddrescue log files

=head1 SYNOPSIS

B<ddrlogmod.pl> [options] block logfile

=head1 DESCRIPTION

B<ddrlogmod.pl> sets the specified block in a GNU ddrescue log file to a specified type.
Before the original log file is overwritten, a backup with the name <logfile>.bak is created.

=head1 ARGUMENTS

=over 7

=item B<block>

block whose state is to be changed, with 0 being the first

=item B<logfile>

the ddrescue logfile to modify

=back

=head1 OPTIONS

=over 7

=item B<-h>, B<--help>

display this help and exit

=item B<-b>, B<--block-size>=<bytes>

block size in bytes [default 512]

=item B<-l>, B<--block-type>=<type>

set type of block (?*/-+) [default ?]

=back

=head1 AUTHOR

Burkart Lingner, 2011

=cut
