#!/usr/bin/perl -w
#############################
# noworries -
# m@perlmeister.com
#############################
use strict;
use Sysadm::Install qw(:all);
use File::Find;
use SGI::FAM;
use Log::Log4perl qw(:easy);
use File::Basename;
use Getopt::Std;
use File::Spec::Functions
qw(rel2abs abs2rel);
use DateTime;
use
DateTime::Format::Strptime;
use Pod::Usage;

my $RCS_DIR =
"$ENV{HOME}/.noworries.rcs";
my $SAFE_DIR =
"$ENV{HOME}/noworries";

my $CI = "ci";
my $CO = "co";
my $RLOG = "rlog";

getopts( "dr:wl",
\my %opts );

mkd $RCS_DIR
unless -d $RCS_DIR;

Log::Log4perl->easy_init({
category => 'main',
level => $opts{d}
? $DEBUG
: $INFO,
file => $opts{w} &&
!$opts{d}
? "/tmp/noworries.log"
: "stdout",
layout => "%d %p %m%n"
}
);

if ( $opts{w} ) {
INFO "$0 starting up";
watcher();

} elsif(
$opts{r} or $opts{l} ) {

my ($file) = @ARGV;
pod2usage("No file given")
unless defined $file;

my $filename =
basename $file;

my $absfile =
rel2abs($file);
my $relfile =
abs2rel( $absfile,
$SAFE_DIR );

my $reldir =
dirname($relfile);
cd "$RCS_DIR/$reldir";

if ( $opts{l} ) {
rlog($filename);
} else {
sysrun(
$CO, "-r$opts{r}",
"-p", $filename
);
}
cdback;

} else {
pod2usage(
"No valid option given");
}

#############################
sub watcher {
#############################
cd $SAFE_DIR;

my $fam = SGI::FAM->new();
watch_subdirs( ".", $fam );

while (1) {
# Block until next event
my $event =
$fam->next_event();

my $dir =
$fam->which($event);
my $fullpath =
$dir . "/" .
$event->filename();

# Emacs temp files
next
if $fullpath =~ /~$/;

# Vi temp files
next if $fullpath =~
/\.sw[px]x?$/;

DEBUG "Event: ",
$event->type, "(",
$event->filename, ")";

if ( $event->type eq
"create"
and -d $fullpath ) {
DEBUG "Adding monitor",
" for directory ",
$fullpath, "\n";
$fam->monitor(
$fullpath);
}
elsif ( $event->type =~
/create|change/
and -f $fullpath ) {
check_in($fullpath);
}
}
}

#############################
sub watch_subdirs {
#############################
my ($start_dir, $fam) = @_;

$fam->monitor($start_dir);

for my $dir (
subdirs($start_dir) ) {
DEBUG "Adding monitor ",
"for $dir";
$fam->monitor($dir);
}

return $fam;
}

#############################
sub subdirs {
#############################
my ($dir) = @_;

my @dirs = ();

find sub {
return unless -d;
return if /^\.\.?$/;
push @dirs,
$File::Find::name;
}, $dir;

return @dirs;
}

#############################
sub check_in {
#############################
my ($file) = @_;

if ( !-T $file ) {
DEBUG "Skipping non-",
"text file $file";
return;
}

my $rel_dir =
dirname($file);
my $rcs_dir =
"$RCS_DIR/$rel_dir/RCS";

mkd $rcs_dir
unless -d $rcs_dir;

cd "$RCS_DIR/$rel_dir";
cp "$SAFE_DIR/$file", ".";
my $filename =
basename($file);

INFO "Checking $filename",
" into RCS";
my ($stdout, $stderr,
$exit_code) = tap(
$CI, "-t-",
"-m-", $filename
);
INFO "Check-in result: ",
"rc=$exit_code ",
"$stdout $stderr";

($stdout, $stderr,
$exit_code) = tap(
$CO, "-l", $filename);
cdback;
}

#############################
sub time_diff {
#############################
my ($dt) = @_;

my $dur =
DateTime->now() - $dt;

for (
qw(weeks days hours
minutes seconds)) {
my $u =
$dur->in_units($_);
return "$u $_" if $u;
}
}

#############################
sub rlog {
#############################
my ($file) = @_;

my ( $stdout, $stderr,
$exit_code )
= tap( $RLOG, $file );

my $p =
DateTime::Format::Strptime
->new( pattern =>
'%Y/%m/%d %H:%M:%S' );

while ($stdout =~
/^revision\s(\S+).*?
date:\s(.*?);
(.*?)$/gmxs) {

my ($rev, $date, $rest)
= ($1, $2, $3);

my ($lines) = ($rest =~
/lines:\s+(.*)/);
$lines ||=
"first version";

my $dt =
$p->parse_datetime(
$date);

print "$rev ",
time_diff($dt),
" ago ($lines)\n";
}
}

__END__

=head1 NAME

noworries - Dev Safety Net

=head1 SYNOPSIS

# Print previous version
noworries -r revision file

# List all revisions
noworries -l file

# Start the watcher
noworries -w
