#! /usr/bin/perl

# Copyright (C) 2007  Nick Urbanik <nicku@nicku.org>

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

use warnings;
use strict;
use Getopt::Long;
use Fcntl qw{:DEFAULT :seek};
use Time::HiRes qw{sleep};

my $numparts = 100;
my $debug;

sub usage {
    ( my $prog = $0 ) =~ s{.*/}{};
    print <<END_USAGE;
usage: $prog [--numparts=n] file [file ...]

Read the file(s) provided on the command line, dividing each into $numparts
random sized pieces, printing to standard output.

Used to simulate a dhcp server writing its lease file, for example.
--numparts changes the number of parts into which each file is divided.
Default: $numparts parts
END_USAGE
}

GetOptions(
    'numparts=i' => \$numparts,
    'debug' => \$debug,
) or usage;

FILE: foreach my $file ( @ARGV ) {
    my $length = -s $file or die "File '$file' is of zero length\n";
    print "length = $length\n" if $debug;
    my @positions = ( $length );
    ++$numparts;
    for ( my $i = 1; $i < $numparts; ++$i ) {
	$positions[ $i ] = $positions[ $i - 1 ] - int rand ( 2 * $positions[ $i - 1 ] / ( $numparts - $i ) );
	$positions[ $i ] = 0 if $positions[ $i ] < 0;
    }
    $positions[ -1 ] = 0;

    @positions = reverse @positions;
    print join( "\n", @positions ), "\n" if $debug;

    open my $fh, '<', $file or die "Cannot open file '$file': $!";
    sysopen $fh, $file, O_RDONLY or die "Cannot sysopen file '$file': $!";
    my $lastpos = shift @positions;
  POS: foreach my $pos ( @positions ) {
	sysseek $fh, $lastpos, SEEK_SET or die "Cannot seek to position '$pos' in '$file': $!";
	my $to_read = $pos - $lastpos;
	next POS unless $to_read;
	my $buffer;
	my $bytes_read = sysread $fh, $buffer, $to_read;
	die "Error reading $to_read bytes from $file at position $lastpos: $!\n"
	    unless defined $bytes_read;
	die "Read $bytes_read bytes instead of $to_read from $file at position $lastpos\n"
	    unless $bytes_read == $to_read;
	my $written = syswrite STDOUT, $buffer;
	$written == $to_read
	    or die "Could write only $written bytes instead of $to_read bytes\n";
	$lastpos = $pos;
	sleep rand 1;
    }
    close $fh or die "Cannot close file '$file': $!";
}
