#!/usr/bin/perl -w
# $Id: makeppclean,v 1.18 2010/09/13 21:17:25 pfeiffer Exp $

package Mpp;

use strict;

use POSIX ();

#
# Exiting
#
# Do this early, because the END block defined below shall be the first seen
# by perl, such that it is the last executed.  It leaves the process via
# POSIX::_exit, so that no expensive garbage collection of Mpp::Files occurs.
# All other places can use die or normal exit.  If you define additional END
# blocks in any module, you must take care to not reset $?.
#
END {
  close STDOUT; close STDERR;
  POSIX::_exit $?;
}

our $datadir;
BEGIN {
  our $VERSION = '@VERSION@';
#@@setdatadir
#
# Find the location of our data directory that contains the auxiliary files.
# This is normally built into the program by install.pl, but if makepp hasn't
# been installed, then we look in the directory we were run from.
#
  $datadir = $0;		# Assume it's running from the same place that
				# we're running from.
  unless( $datadir =~ s@/[^/]+$@@ ) { # No path specified?
				# See if we can find ourselves in the path.
    foreach( split( /:/, $ENV{'PATH'} ), '.' ) {
				# Add '.' to the path in case the user is
				# running it with "perl makepp" even if
				# . is not in his path.
      if( -d "$_/Mpp" ) {	# Found something we need?
	$datadir = $_;
	last;
      }
    }
  }
  $datadir or die "makepp: can't find library files\n";

  $datadir = eval "use Cwd; cwd . '/$datadir'"
    if $datadir =~ /^\./;	# Make it absolute, if it's a relative path.
#@@
  unshift @INC, $datadir;
}

use Mpp::Utils;
use File::Path qw(rmtree);
use Mpp::File;
use Mpp::FileOpt;
use Mpp::Text ();


my $verbose;

sub print_msg {
  foreach my $str (@_, ($_[-1] =~ /\n\z/) ? () : "\n") {
    my $str_to_print = $str;
    ref($str_to_print) and $str_to_print = absolute_filename $str;
    print STDERR $str_to_print;
  }
}
sub print_error {
  print STDERR "$Mpp::progname: error: ";
  &print_msg;
}

sub print_warning {
  print STDERR "$Mpp::progname: warning: ";
  &print_msg;
}
sub print_log {
  &print_msg if $verbose;
}

my $n_files_removed = 0;
my $recurse;

sub perform(&) {
  my $code = $_[0];
  eval { &$code; };

  if ($@) {			# Did we die() somehow?
    print_error $@;		# Print the error message.
    close STDOUT; close STDERR;	# Flush these too, or else we'll have problems.
    exit 1;
  }

  print_log "$n_files_removed files removed";
  $n_files_removed or print "$Mpp::progname: no files removed\n";

  exit 0;
}

my @info_strings;		# Our deletion criteria.
my @deletable;			# Files must be first remembered for deletion,
				# because if we deleted them right away, and
				# then later found a symlink to one, that might
				# no longer have a checkable build info.
my @deletable_dirs;
sub deletable {
  return 1 if -l &Mpp::File::build_info_fname && !-e _; # Stale inter-build-cache symlink?
  defined and return 1
    for Mpp::File::build_info_string $_[0], @info_strings;
  if( $_[0]{TEMP_BUILD_INFO} ) { # Stale symlink?
    return if $_[0]{TEMP_BUILD_INFO}{SYMLINK} ne (readlink &Mpp::File::absolute_filename or '');
    defined and return 1
      for @{$_[0]{TEMP_BUILD_INFO}}{@info_strings};
  }
  $_[0]{NAME} =~ /^\.makepp_(?:log|testfile)/; # Always clean leftovers.
}

sub remove_if_built_file {
  if( &deletable ) {
    if( &is_dir ) {
      unshift @deletable_dirs, $_[0];
    } else {
      print_log "Removing ", $_[0];
      push @deletable, &relative_filename, &Mpp::File::build_info_fname;
      $n_files_removed++;
    }
    unless( $_[0]{'..'}{MAKEPP_DELETABLE} ) { # Remember to check .makepp later.
      $_[0]{'..'}{MAKEPP_DELETABLE} = 1;
      my $makepp = file_info $Mpp::File::build_info_subdir, $_[0]{'..'};
      unshift @deletable_dirs, $makepp if is_dir $makepp;
    }
  }
}

my $leave_src_info;
sub remove_contents_if_built {
  my( $finfo ) = @_;
  &Mpp::File::read_directory;
  for my $name (keys %{$finfo->{DIRCONTENTS}}) {
    next if $name eq $Mpp::File::build_info_subdir;
    my $file = $finfo->{DIRCONTENTS}{$name};
    remove_if_built_file $file;
    remove_contents_if_built( $file ) if $recurse and is_dir $file;
  }
  unless( $leave_src_info ) { # Remember to check .makepp later.
    my $makepp = file_info $Mpp::File::build_info_subdir, $finfo;
    if( is_dir $makepp ) {
      unless( $finfo->{MAKEPP_DELETABLE} ) {
	$finfo->{MAKEPP_DELETABLE} = 1;
	unshift @deletable_dirs, $makepp;
      }
      $makepp->{DELETABLE} = 1; # Delete unconditionally.
    }
  }
}

perform {
  Mpp::Text::getopts
    ['b', qr/(?:only[-_]?)?(?:build[-_]?)?cache[-_]?(?:link|file)s/, undef, 0,
     sub { push @info_strings, 'LINKED_TO_CACHE' }],

    ['l', qr/leave[-_]?src[-_]?info/, \$leave_src_info],

    ['R', qr/(?:only[-_]?)?repository[-_]?links/, undef, 0,
     sub {push @info_strings, 'FROM_REPOSITORY' }],
    ['r', qr/recurs(?:iv)?e/, \$recurse],

    [qw(v verbose), \$verbose],

    splice @Mpp::Text::common_opts;

  if( @info_strings ) {
    $leave_src_info = 1;	# Actually leave all other infos.
  } else {
    @info_strings = qw(BUILD_SIGNATURE FROM_REPOSITORY);
  }
  if( @ARGV ) {
    for my $arg (@ARGV) {
      my $finfo = file_info $arg;
      remove_if_built_file $finfo;
      remove_contents_if_built $finfo if is_dir $finfo;
    }
  } else {
    remove_contents_if_built file_info '.';
  }
  unlink @deletable;		# Perform the bulk of the work.
  for( @deletable_dirs ) {
    unless( $_->{DELETABLE} ) {	# Must check if empty.
      $_->{DIRCONTENTS} = {};	# Maybe lots deleted w/o telling Mpp::File.
      Mpp::File::read_directory $_; # Take note of all that is gone by now.
      delete $_->{DIRCONTENTS}{$Mpp::File::build_info_subdir};
      next if keys %{$_->{DIRCONTENTS}}; # There are other files left.
    }
    print_log "Removing ", $_, '/' if $_->{NAME} ne $Mpp::File::build_info_subdir;
    rmtree relative_filename $_;
  }
};

__DATA__
Usage: makeppclean [options] [path ...]

All named files, and all files in named directories, that were generated by
makepp are removed.  'path ...' defaults to '.'.  Valid options are:

-A filename, --args-file=filename, --arguments-file=filename
    Read the file and parse it as possibly quoted whitespace- and/or
    newline-separated options.
-b, --build-cache-links, --only-build-cache-links
    Remove only links or files from a build cache.
-?, -h, --help
    Print out a brief summary of the options.
-l, --leave-src-info
    Leave the build info directory below each named directory.
-R, --repository-links, --only-repository-links
    Remove only links pointing to a repository.
-r, --recurse, --recursive
    Descend each named directory recursively.
-v, --verbose
    Enable verbose output to STDERR.
-V, --version
    Print out the version number.

Look at @htmldir@/makeppclean.html for more details,
or at http://makepp.sourceforge.net/@BASEVERSION@/makeppclean.html
or type "man makeppclean".
