#!/usr/bin/perl

use strict;
use Cwd;
use File::Basename;

# We are going to need the current working directory
# that this script is run from.

# This will go into the name of the file and the
# cfengine class within the file
my $working_cwd = cwd;
my $working_basename = basename $working_cwd;

#print "Cwd = $working_basename\n";

scandir('.');

########################################
sub scandir {
  my ($currentdir) = @_;
  chomp $currentdir;

  # Used after svn update or checkout
  # Get the properties for files and directories and a list of symlinks
  # and sets them appropriately

  # We have to assume that svn checkout has succesfuly created the directory
  # structure and populated it with files.

  print "Current dir: $currentdir\n";
  ########################################
  # Gather up the contents but neither . nor ..
  # I also want to *not* match items that are in @ignorelist
  # . and .. and anything in ignorelist
  opendir(my $d,$currentdir) or die "$currentdir: $!";
  my @contents = grep { !m/^\.\.?$/ } readdir $d;
  ########################################

  # First thing to do is to determine what we are going to ignore
#  print "Getting ignores\n";
#  print "<$currentdir>\n";
  my @ignorelist = `svn propget svn:ignore "$currentdir"`;
  chomp @ignorelist;
  my %ignore;
  $ignore{$_} = undef foreach(@ignorelist);

  ########################################
  # This list will consist of strings of the form
  # symlink -> target.
  # Unfortunately the svn propget command returns extra fluff at the end so we need to
  # skip that.
#  print "Getting dir:symlinks property\n";
#  print "<$currentdir>\n";
  my @symlink_list = grep { m/^(.+)\s->\s(.+)$/ }
    `svn propget dir:symlinks "$currentdir"`;
  chomp @symlink_list;
  ########################################

  my @device_list = `svn propget dir:devices "$currentdir"`;
  chomp @device_list;

#  print $_ . "\n" foreach @device_list;

  ########################################

  # the list of directories to process should not include:
  # things that are ignored
  # .svn directories
  # .. nor . directories
  my @directory_list =
    grep { -d "$currentdir/$_"
	     && ! -l "$currentdir/$_"
	       && !/\.svn$/
		 && not exists $ignore{$_} }
      @contents;

  # The list of files to process shouldn't contain items listed in the ignores
  my @file_list =
    grep { -f "$currentdir/$_"
	     && ! -l "$currentdir/$_"
	       && not exists $ignore{$_} }
      @contents;

  ########################################

  dosymlinks( $currentdir, @symlink_list ) if @symlink_list;

  ########################################

  dodevices( $currentdir, @device_list ) if @device_list;

  ########################################

  dodirectories( $currentdir, @directory_list) if @directory_list;

  ########################################

  dofiles( $currentdir, @file_list) if @file_list;

  ########################################
  # Recursive call
  # directory_list doesn't contain any .. . or .svn
  foreach my $sub (@directory_list) {
    scandir ("$currentdir/$sub");
  }
}
# End scandir

sub dosymlinks {
  my ($currentdir, @symlink_list) = @_;

  foreach my $symlink_target (@symlink_list) {
    # extract the name of the symlink and its target:
    my ($symlink,$target) = ( $symlink_target =~ /^(.+)\s\-\>\s(.+)$/ );
# print "Symlink = <$symlink>\n";
# print "Target  = <$target>\n";
    # We have to stick $currentdir at the front so that it gets
    # created in the right place
    my $linkname = $currentdir . "/" . $symlink;
    symlink($target, $linkname);
  }
}
# End dosymlinks

sub dofiles {
  my ($currentdir, @files) = @_;
#  print "Files:\n", ( join("\n", @files), "\n");

  foreach my $file (@files) {
    my $thisfile = "$currentdir" . "/" . "$file";
    ########################################
#    print "Getting svn status\n";
    next if grep { m/^(\?|I).+$/ } (`svn status "$thisfile"`);
    ########################################
#    print "Getting file:mode property\n";
    my $file_mode = `svn propget file:mode "$thisfile"`;
    chomp $file_mode;
    # Bizzarely enough, perl expects the first parameter of chmod to be decimal
#  print "Mode of $thisfile = $file_mode\n";
    chmod oct($file_mode), "$thisfile";
    # ah, I do need to take ignores into account? to avoid symlinks?
    # or maybe just grep them out? They shoulnt have made it this far?
#    print "Getting file:gid property\n";
    my $file_gid  = `svn propget file:gid "$thisfile"`;
    chomp $file_gid;
#    print "GID of $thisfile = $file_gid\n";
#    print "Getting file:uid property\n";
    my $file_uid  = `svn propget file:uid "$thisfile"`;
    chomp $file_uid;
#    print "UID of $thisfile = $file_uid\n";
    chown $file_uid, $file_gid, "$thisfile";
  }
}
# End dofiles

sub dodirectories{
  my ($currentdir, @directories) = @_;
#  print "Dirs:\n", ( join("\n", @directories), "\n");

  foreach my $directory (@directories) {
    my $thisdir = "$currentdir" . "/" . "$directory";
    ########################################
#    print "Getting svn status\n";
    next if grep { m/^(\?|I).+$/ }
      (`svn status "$thisdir"`);
    ########################################
#    print "Getting dir:mode property\n";
    my $dir_mode = `svn propget dir:mode "$thisdir"`;
    chomp $dir_mode;
    # Bizzarely enough, perl expects the first parameter of chmod to be decimal
    # print "Mode of $directory = $dir_mode\n";
    chmod oct($dir_mode), $thisdir;
    # ah, I do need to take ignores into account? to avoid symlinks?
    # or maybe just grep them out? They shoulnt have made it this far?
#    print "Getting dir:gid property\n";
    my $dir_gid  = `svn propget dir:gid "$thisdir"`;
    chomp $dir_gid;
    # print "GID of $directory = $dir_gid\n";
#    print "Getting dir:uid property\n";
    my $dir_uid  = `svn propget dir:uid "$thisdir"`;
    chomp $dir_uid;
#    print "UID of $directory = $dir_uid\n";
    chown $dir_uid, $dir_gid, "$thisdir";
  }
}
# End dodirs

sub dodevices {
  my ($currentdir, @device_list) = @_;

  foreach my $deviceline (@device_list) {
    # individual device lines look like this:
    # name block_or_char major minor -m octal_mode

    exit unless $deviceline;

    my ($device_name, $device_type, $major, $minor, $flag, $mode) = $deviceline =~ m/^(\w+)\s(\w)\s(\d+)\s(\d+)\s(-m)\s(\d+)$/;

#    print "$device_name, $device_type, $major, $minor, $flag, $mode\n";
#    print "Device line = <mknod $deviceline>\n";

    # Make sure that its removed as we don't guarantee that its correct even if it
    # does exist
    unlink "$currentdir/$device_name";

    my @mknod_command =
      ("mknod", "$currentdir/$device_name", "$device_type", "$major", "$minor", "$flag", "$mode");

    print "mknod command = ", ( join("\t", @mknod_command), "\n");

    system(@mknod_command) == 0
      or die "Problem creating device with $deviceline: $!\n";
#    `mknod $deviceline`;
  }
}
# End dodevices

