#!/usr/bin/perl -w
#
# websrccopy
#
# 'Copy' the source files for a web site by symlinking where possible.
#
# Copyright (C) 1999-2000 S Morphet <smorphet@iee.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.
#

# Modules.
use File::Copy 'cp';
use FileHandle;

# Function to print usage information.
sub DieUsage {
  print "\n";
  print "$id_name $id_version ($id_tag)\n" .
    "Usage: $id_name [options] source-dir\n\n";
  print "Options: -v \t\t Be verbose\n";
  print "\t -vv\t\t Be very verbose\n";
  print "\t -d dir \t The destination directory (default \".\")\n";
  print "\t -C file \t Name of files to copy.\n";
  print "\t -L file \t Name of files or dirs to symlink.\n";
  print "\t -X file \t Name of dirs NOT to recurse into.\n";
  print "\t -n\t\t Don't copy or link anything.\n\t\t\t " .
    "(Use with -v to see what would have happenned.)\n";
  print "\t -version \t Print version info and exit.\n";
  print "\n";
  print "-C, -L and -X may be specified multiple times.\n\n";
  exit;
}

# Create a directory if it doesn't exist.
# Argument, the directory name.
sub makedir {
  my ($dir) = @_;
  if( ! -e $dir ) {
    print "Creating directory $dir.\n" if $verbose;
    mkdir( $dir, 0775 ) if $dostuff;
  }
}


# Expand a filename in a directory, and store the results in a hash.
sub ExpandFilenameIntoHash {
  my ($filename, $srcdir, $hashref) = @_;
  my @explist;
  my $earg;

  # Expand the filename.
  @explist = map { glob($_) } "$srcdir/$arg";

  # Add each expanded filename to the hash.
  foreach $earg ( @explist ) {
    # Remove the source directory from the name.
    $earg =~ s/$srcdir\/(.*)/$1/;

    # Add the name to the hash.
    $$hashref{$earg} = 1;
  }
}


#########################
## Main program start. ##
#########################

# Get information from rcs.
# Spaces before the closing quotes are preserved by RCS and stop
# emacs syntax highlighting thinking there's a variable called $'
$programid = '$Id: websrccopy,v 1.8 2000/06/11 00:37:37 sdm Exp $ ';
$programid =~ /\$Id: (.*?),v (.*?) (.*?) /;
$id_name = $1;
$id_version = $2;
$id_date = $3;

$tagname   = '$Name: Guava-1_0_3 $ ';
$tagname   =~ /\$Name: (.*?) /;
if( $1 ne "" ) {
  $id_tag = $1;
}else{
  $id_tag = "none";
}

# Process arguments specified on command line.
$verbose = 0;
$veryverbose = 0;

$dostuff = 1;

%tocopy = ();
%tolink = ();
%norecurse = ();

@copyargs = ();
@linkargs = ();
@norecurseargs = ();

$recurseargs = "";

$destdir = ".";

$numargs = $#ARGV;
while( ($numargs >= 0) and $ARGV[0] =~ /^-/ )
  {  
    if( $ARGV[0] eq "-v" ) {
      $verbose += 1;
      $recurseargs .= " -v";
    }
    elsif( $ARGV[0] eq "-vv" ) {
      $veryverbose += 1;
      $verbose += 1;
      $recurseargs .= " -vv";
    }
    elsif( $ARGV[0] eq "-d" ) {
      shift;
      $numargs -= 1;
      $destdir = $ARGV[0];
    }
    elsif( $ARGV[0] eq "-n" ) {
      $dostuff = 0;
      $recurseargs .= " -n";
    }
    elsif( $ARGV[0] eq "-C" ) {
      shift;
      $numargs -= 1;
      push( @copyargs, $ARGV[0] );
      $recurseargs .= " -C \"$ARGV[0]\"";
    }
    elsif( $ARGV[0] eq "-L" ) {
      shift;
      $numargs -= 1;
      push( @linkargs, $ARGV[0] );
      $recurseargs .= " -L \"$ARGV[0]\"";
    }
    elsif( $ARGV[0] eq "-X" ) {
      shift;
      $numargs -= 1;
      push( @norecurseargs, $ARGV[0] );
      $recurseargs .= " -X \"$ARGV[0]\"";
    }
    elsif( $ARGV[0] eq "-version" ) {
      print "$id_name, version $id_version, release $id_tag, $id_date.\n";
      exit;
    }
    else {
      print "Unknown option $ARGV[0]\n";
      DieUsage;
    }
    
    $numargs -= 1;
    shift;
  }

DieUsage if( $numargs != 0 );
$srcdir = $ARGV[0];


# Strip trailing slash off the dir name.
$srcdir =~ s/(.*)\/$/$1/;


# Copy the filename args into hashes.

# Copy names.
foreach $arg (@copyargs) {
  ExpandFilenameIntoHash( $arg, $srcdir, \%tocopy );
}

# Link names.
foreach $arg (@linkargs) {
  ExpandFilenameIntoHash( $arg, $srcdir, \%tolink );
}

# Norecurse names.
foreach $arg (@norecurseargs) {
  ExpandFilenameIntoHash( $arg, $srcdir, \%norecurse );
}


print "websrccopy: Reading input directory: $srcdir\n" if $verbose;

# Check that the destination directory exists.
# makedir checks $dostuff, but we want to inhibit even the message here.
makedir( $destdir ) if $dostuff;

# Read the files in the current directory.
@files = <$srcdir/*>;

# Process each file.
foreach $file (@files) {
  print "Testing file $file " if $veryverbose ;
  
  # Is this file a directory?
  if( -d $file ) { $isdir = 1; } else { $isdir = 0; }
  
  # We need to search hashes for the basename, not the whole thing...
  $basename = `basename $file`;
  
  # Remove whitespace from the basename.
  $_ = $basename;
  /\s*(.*)\s*/;
  $basename = $1;

  print "($basename)\n" if $veryverbose;

  # Check if this is a file to copy.
  if( defined $tocopy{$basename} ) {
    print "Copying $file to $destdir/$basename\n" if $verbose;
    cp( "$file", "$destdir/$basename" ) if $dostuff;
  }
  # Or a dir or file to link.  Linked dirs won't be recursed into.
  elsif( defined $tolink{$basename} ) {
    print "Linking $file to $destdir/$basename\n" if $verbose;

    # We could use the following, which ensures that a symlink
    # isn't created inside the symlinked directory, if it already
    # exists.
    #system "ln -s -nf $file $destdir/$basename";

    # Alternatively, we can do it ourselves, which may be
    # more portable, since there may be ln(1) implementations
    # that do not have the -n and -f options.
    if( -l $file )
      {
	# Remove the symlink.
	print "Removing existing symlink ($file).\n" if $verbose;
	unlink( $file ) if $dostuff;
      }
    
    # Link the dir.
    symlink( "$file", "$destdir/$basename" ) if $dostuff;
  } 

  # Or a dir that we can recurse into.
  elsif( $isdir && (!defined $norecurse{$basename}) ) {
    
    $subdest = "$destdir/$basename";

    # Create the destination directory if it doesn't exist.
    makedir( $subdest );

    # Run this program recursively in the subdirectory.
    print "Running: $0 $recurseargs -d $subdest $file\n" if $verbose;

    # Flush verbose output before running another program.
    STDOUT->flush();
    $rc = system "$0 $recurseargs -d $subdest $file";

    if( $rc ) {
      die "$id_name: Failed to run $0 in $subdest.\n";
    }
  }

}
