#! /usr/bin/perl

# --- MODULES ---
use File::Find;
use File::Basename;
use strict;
use warnings;

# --- CONFIG ---

#
# These are pre-compiled pattern, add new ones the same way
#
my @ignore = (
	      qr{\.svn},
	      qr{.+~},
	     );
# These are the fileslists where all files should be listed...
my @filelists = (
		 "files.txt"
		);

#
# --- GLOBAL VARS ---
#
our (%real_files, %files, %all_files, @files, $key, $name, $type, $target, 
     @targets, @packages,
     $header, $first);

#
# --- SUBS ---
#
sub process_file {
  my($name) = $File::Find::name;
  foreach my $pat (@ignore) {
    return if $name =~ /$pat/;
  }
  $name =~ s/^$target\///;
  $real_files{$name} = 1;
}

#
# return all keys from the first hash that are not present in the second hash
#
sub weedout {
  my ($one, $two) = @_;
  grep { !defined($two->{$_}) } keys(%$one);
}

sub add_dir {
  my ($name) = @_;
  my($dir);
  my($orig_name) = dirname($files{$name});
  while ($name =~ /.+/) {
    $dir = dirname($name);
    last if $dir eq "." || defined $files{$dir};

    $files {$dir} = $orig_name;
    $name = $dir;
  }
}

sub header {
  ($header) = @_;
  $first = 1;
}
sub normal_print {
  my ($string) = @_;
  print $header if $first;
  $first = 0;
  print "$string";
}
sub verbose_print {
  my ($string) = @_;
  print $header if $first;
  $first = 0;
  print "$string\n";
}

# --- MAIN ---

my($selected_packets, $realname);

#
# command line handling
#
if (@ARGV == 0) {
  die "usage: mklinks.pl target-dir package1 package2 ..."
}
($target, @packages) = @ARGV;

if (! -d $target) {
  mkdir $target || die "can't create target dir '$target'";
}

if ($#packages < 0) {
  open(PACKETS, "$target/.packet_list") || die "can't open packet list, please specify packets on the command line";
  while (<PACKETS>) {
    chomp;
    push(@packages, $_);
  }
} else {
  open(PACKETS, ">$target/.packet_list") || die "can't open packet list";
  foreach my $packet (@packages) {
    print PACKETS "$packet\n";
  }
}
close PACKETS;
$selected_packets = join("|", @packages);
print "Using '@packages' ('$selected_packets')\n";

#
# clean up unix dir
#
if (-d "$target/unix") {
  print "Cleaning up unix dir\n";
  system("make -C $target/unix clean > /dev/null");
  foreach $name (<$target/unix/*.d>) {
    unlink $name;
  }
  unlink "$target/unix/gen_init_cpio";
  unlink "$target/unix/gen_init_tar";
  unlink "$target/unix/mkfli4l";
}

#
# read files.txt
#
print "Reading file list...\n";
header("\n");
foreach my $list (@filelists) {
  my($type, $packet, $file);
  open (FILES, "<", $list) or die "can't open '$list': $!";

  while (<FILES>) {
    chomp;
    /^(.)\s+([^\/]*)\/(.*)$/o;
    $type = $1;
    $packet = $2;
    $file = $3;
    if ($type eq "I" || $type eq "i") {
    	normal_print "    ignoring $packet/$file\n";
	next;
    }
		
    if (defined ($all_files{$file})) {
      normal_print "    redefinition of $file ($all_files{$file} -> $_)\n" if system "cmp $packet/$file $all_files{$file} > /dev/null";
    }
    $all_files{$file} = "$packet/$file";
    if  ($packet =~ /^($selected_packets)$/) {
      $files{$file} = "$packet/$file";
      add_dir($file);
    }
  }
  close FILES;
}

#
# read files in directories
#
print "\nSearching for files...\n";
my(@target_str) = ("config",
		   "changes", 
		   "check", 
		   "doc", 
		   "opt", 
		   "unix",
		   "img",
		   "src",
		   "windows",
		   "COPYING.GPL", "mkfli4l.bat", "rename.sh", "mkfli4l.sh", 
		   "readme.html", "links.html", "readme.txt", "version.txt"
		  );
foreach $name (@target_str) {
  push(@targets, "$target/$name") if -e "$target/$name";
}
find(\&process_file, @targets) if $#targets >= 0;

#
# check for links and directories from an older setup
#
header "\nRemoving links from an earlier setup:\n\n";
foreach $name (sort(weedout(\%real_files,\%files))) {
  if (-l "$target/$name" && (defined($all_files{$name}))) {
    normal_print "   l $target/$name\n";
    unlink "$target/$name";
    delete $real_files{$name};
  }
}

header "\nRemoving directories from an earlier setup:\n\n";
foreach $name (sort(weedout(\%real_files,\%files))) {
  if (-d "$target/$name") {
    while ("$name" ne ".") {
      last if ! rmdir "$target/$name";
      normal_print "   d $target/$name\n";
      delete $real_files{$name};
      $name = dirname ("$name");
    }
  }
}

#
# check for files which are in the local dir, but not in the repository
#
header "\n\nNew or Old files:\n\n";
foreach $name (sort(weedout(\%real_files,\%files))) {
  if (-l "$target/$name") {
    if (! -e readlink("$target/$name")) {
      delete $real_files{$name};
      normal_print ("    $target/$name - removing stale link\n"); 
      unlink ("$target/$name");
      next;
    }
  }
  
  normal_print "    $target/$name\n";
}

#
# check for files which are in the local dir, but are no links - they
# may contain local changesnot in the repository
#
header "\nLocal files:\n\n";
foreach $name (sort keys %real_files) {
  next if ! defined($all_files{$name}) || -l "$target/$name" || -d "$target/$name";

  normal_print "    $target/$name\n";
}

#
# check for files which are in the file list but not in the local dir
# - add them
#
my($cwd) = `pwd`;
chomp($cwd);
header "\nMissing links:\n\n";
foreach $name (sort(weedout(\%files,\%real_files))) {
  if (! -e  "$target/$name") {
    normal_print "    $name\n";
    if (-d $files{$name}) {
      mkdir("$target/$name", 0777) unless -d "$target/$name";
    }
    else {
      symlink("$cwd/$files{$name}", "$target/$name") || print "cannot create symlink $target/$name -> $cwd/$files{$name}\n" ;
    }
  }
}

print "done\n";
