#!/usr/bin/perl -w

use Getopt::Std;
use File::Basename;
use Cwd qw(abs_path getcwd);

our $prg = $0;

$prg =~ s:(.*)/::;
$0 = "$prg @ARGV";
$| = 1;

$usage = "$prg: fex new created or modified files
usage: $prg [-v] [-n] [-a] [-A] [-t NAME] [-k DAYS] [-w SECONDS] [RECIPIENT]
options:
  -v  verbose mode
  -n  do not send notification email
  -a  also fex files where attributes have been modified
  -A  fex ALL files, not only new ones
  -t  use transport NAME, must have suffix .tar .tgz or .zip
  -k  keep DAYS on server (default: 1)
  -w  wait SECONDS to collect new files (default: 10)
example:
  $prg -v -a framstag\@rus.uni-stuttgart.de
";

$ENV{FUA} = 'autofex';

$opt_h = $opt_v = $opt_n = $opt_a = $opt_A = 0;
$opt_k = 1;
$opt_w = 10;
$opt_t = basename(getcwd()).'.tar';
getopts('hvnaAt:w:k:') or die $usage;

if ($opt_h) {
  print $usage;
  exit;
}

$wait = 10;
$wait = $1 if $opt_w =~ /^(\d+)$/;
$keep = 1;
$keep = $1 if $opt_k =~ /^(\d+)$/;

$lsof    = searchpath('lsof')        or die "$prg: lsof not installed\n";
$inotify = searchpath('inotifywait') or die "$prg: inotifywait not installed\n";

$inotify .= ' -qmr -e close_write -e moved_to -e create -e modify';
$inotify .= ' -e attrib' if $opt_a;

@fexsend = ('fexsend');
push @fexsend,'-k',$keep;
push @fexsend,'-n' if $opt_n;
$recipient = join(',',@ARGV) || '.';

if ($opt_t !~ /\.(tar|tgz|zip)$/) {
  die "$prg: transport name must have suffix .tar .tgz or .zip\n";
}

print "\$ $inotify .\n" if $opt_v;
open $inotify,"$inotify . |" or die "$prg: cannot run inotifywait - $!\n";

$SIG{ALRM} = \&fexsend;
%files = ();

while (<$inotify>) {
  alarm($wait);
  print if $opt_v;
  chomp;
  if (s/ (CREATE|CLOSE|MOVE|ATTRIB)[_,A-Z]* //) {
    unless ($files{$_}) {
      if ((@s = lstat) and $s[3]>1) {
        my $find = "find . -xdev -type f -inum $s[1]";
        print "\$ $find\n" if $opt_v;
        if (open my $find,"$find 2>/dev/null|") {
          while (<$find>) {
            chomp;
            $files{$_}++;
          }
          close $find;
        }
      } else {
        $files{$_}++;
      }
    }
  }
  if ($opt_v and %files) {
    kill 9,$spid if $spid;
    local $SIG{CHLD} = 'IGNORE';
    $spid = fork();
    if (defined $spid and $spid == 0) {
      for (my $i = $wait; $i; $i--) {
        printf "%3d\r",$i;
        sleep 1;
      }
      exit;
    }
  }
}


sub fexsend {
  local $_;
  my @files = keys %files;

  if (@files and @files = grep { -r or -l } sort(@files)) {
    print "\r";
    my @fexsend = @fexsend;
    my $archive = $opt_t;
    if ($opt_A) {
      my @wof = ();
      if ($lsof) {
        if (open my $lsof,"$lsof +D . 2>/dev/null|") {
          while (<$lsof>) {
            if (/^\S+\s+\d+\s+\S+\s+\d+w\w*\s+(\S+\s+){4}(.+)/) {
              push @wof,$2;
            }
          }
          close $lsof;
        }
      }
      push @fexsend,('-#',join('#',@wof)) if @wof;
      @files = ('.');
    } else {
      my @d = localtime time;
      my $t = sprintf('%d%02d%02d_%02d%02d%02d',
                      $d[5]+1900,$d[4]+1,$d[3],$d[2],$d[1],$d[0]);
      $archive =~ s/(\.\w+)$/_$t$1/;
      @files = map { s:^\./::;$_ } @files;
    }
    push @fexsend,('-A',$archive,@files,$recipient);
    print "\$ @fexsend\n" if $opt_v;
    system(@fexsend);
    print "\n";
  }
  %files = ();
};


sub searchpath {
  my ($prg,$path,@PATH);

  @PATH = split(':',$ENV{PATH});
  foreach $prg (@_) {
    foreach $path (@PATH) {
      return $prg if -x "$path/$prg";
    }
  }
  return '';
}
