This is the mail archive of the cygwin@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Using a real mirroring tool...


Hi!

Charles Wilson wrote:

> A few notes:
> 
> ...
> 
> rsync can't merge.


Yes, but Michael A. Chases clean_setup.pl can! I allowed myself to add a 
new option to clean_setup.pl, it exports the list of missing files to a 
file. Then you can use wget to get all the missing files.

I attached the modified clean_setup.pl plus a shell scrip to get the 
missing files from a mirror. You have to modify the getmissing.sh to 
choose your mirror and your target download directory. The directory and 
both files must exist the same directory as your setup.exe. Just a quick 
hack, I guarantee nothing!

Bye
    Volker
#!/bin/bash

# Mirror script for cygwin
# Uses Michael A. Chase "cleanup_setup.pl"
# in a modified version (V1.0303)
# and wget

# getmissing.sh and cleanup_setup.pl must reside in the same directory
# as setup.exe

# Files to download
filelist="missing.lst"
# Choose your mirror with directory of setup.ini
mirror="ftp://ftp.inf.tu-dresden.de/software/windows/cygwin32/";
# Cut directories                   1        2       3
cutdir=3
# Name your download directory
mirrordir="_mymirror"

wget --mirror --no-host-directories --cut-dirs=$cutdir --passive-ftp -P $mirrordir ${mirror}setup.ini
./clean_setup.pl -Arch -writelist
wget --mirror --no-host-directories --cut-dirs=$cutdir --passive-ftp -P $mirrordir -i $filelist -B $mirror
./clean_setup.pl -Setup
#! /usr/bin/perl -w

require 5.005;

# Delete old files from Cygwin setup.exe archive subdirectories

# Name of file:  cleanup_setup.pl
#
# Type of file:  Perl script
#
# Author:        Michael A. Chase
#
# Purpose:       Clean out setup.exe archive subdirectories.

# Syntax:        See $HelpText Below
#--------------------------  MODIFICATION HISTORY -----------------------------
$VERSION = '1.0303';
# 020430 V. Quetschke   Add -writelist
#                       Writes list of missing files to missing.lst.
# 020423 M. Chase       Add -source, -install, -H options.
#                       Removed directory names from Missing Files list.
# $VERSION = '1.0301';
# 020417 M. Chase       Stop lc()ing file names during setup.ini parsing.
# $VERSION = '1.0300';
# 020416 M. Chase       Ignore setup.ini while collecting archive file names.
#                       Remove directories in reverse length order.
# $VERSION = '1.0202';
# 020416 M. Chase       Properly ignore directories in obsolete file search.
# $VERSION = '1.0201';
# 020404 M. Chase       Ignore files found more than once in move loop.
#                       Protect against attempts to move files from different
#                          trees over each other in the base tree.
# $VERSION = '1.0200';
# 020326 M. Chase       Properly handle directory case.
#                       Optionally move archives to base directory tree.
# $VERSION = '1.0100';
# 020212 M. Chase       Handle multiple setup.ini files and file sources.
# $VERSION = '1.0000';
# 010422 M. Chase       First draft.

use FindBin qw( $RealBin $RealScript );
use File::Basename qw( &basename &dirname &fileparse );
use File::Find;
use File::Spec;
use File::Path qw( &mkpath );
use File::Copy qw( &copy &move );
use Getopt::Long;

use strict;
use integer;
use vars qw( $VERSION );

# Initialize options
my $bArch    = 0;
my $bMove    = 0;
my $bSetup   = 0;
my $bCurr    = 1;
my $bPrev    = 1;
my $bTest    = 1;
my $bExp     = 1;
my $bInstall = 1;
my $bSource  = 0;
my $bWrite   = 0;
my @sHide    = ();
my @sIgnore  = ();
my $sDir0    = File::Spec -> rel2abs( "." );

# Syntax description
sub usage {
   my ( $sOpt, $sVal, @sMsg ) = @_;

   my $sHelpText = <<END_HELP_TEXT;
Cleanup Cygwin setup.exe package cache directories
syntax: $RealScript [opt]
Opt: ($VERSION)
   -[no]Move    = [Don't] Move archive files to base directory tree ($bMove)
   -[no]Arch    = [Don't] Delete obsolete archives and directories ($bArch)
   -[no]Setup   = [Don't] Delete obsolete setup.ini files ($bSetup), forces -Move
   -[no]curr    = [Don't] Report missing [curr] files ($bCurr)
   -[no]prev    = [Don't] Report missing [prev] files ($bPrev)
   -[no]test    = [Don't] Report missing [test] files ($bTest)
   -[no]exp     = [Don't] Report missing [exp]  files ($bExp)
   -[no]install = [Don't] Report missing install archives ($bInstall)
   -[no]source  = [Don't] Report missing source archives ($bSource)
   -writelist   = Write missing files report (missing.lst) to disk ($bWrite)
   -base dir    = Archive cache base directory ($sDir0)
   -I mask      = Ignore files and directories that match mask, multiple allowed
   -H mask      = Ignore packages that match mask, multiple allowed
      mask is a filename wildcard mask, not a regular expression
Arg:
END_HELP_TEXT
# Balance quotes in here document # ' # "

   my $nRet = 'help' eq $sOpt ? 0 : 0 + $sVal;
   select STDERR if $nRet;
   foreach ( @sMsg, $sHelpText ) { s/\s+$//; print "$_\n"; }
   exit $nRet;
}

# Parse command line
Getopt::Long::config( qw( no_ignore_case no_auto_abbrev require_order ) );
GetOptions(
   'Arch!'       => \$bArch,
   'Move!'       => \$bMove,
   'Setup!'      => \$bSetup,
   'curr!'       => \$bCurr,
   'prev!'       => \$bPrev,
   'test!'       => \$bTest,
   'exp!'        => \$bExp,
   'install!'    => \$bInstall,
   'source!'     => \$bSource,
   'writelist!'  => \$bWrite,
   'base=s'      => \$sDir0,
   'Hide|H=s@'   => \@sHide,
   'Ignore|I=s@' => \@sIgnore,
   'help|h' => \&usage ) or usage( 'die', 1 );
$bMove ||= $bSetup; # Don't remove setup files unless also moving archves
chdir $sDir0 or usage( 'die', 1, "Can't change directory to $sDir0, $!\n" );

# Provide default values for unset options and parameters

# Report arguments
$sDir0 = File::Spec -> rel2abs( "." );
$sDir0 =~ s,\\,/,g;
my $sOpt = '';
$sOpt .= "\nMoving archives to base directory tree" if $bMove;
$sOpt .= "\nDeleting unused files and empty directores" if $bArch;
$sOpt .= "\nDeleting obsolete setup.ini files" if $bSetup;
$sOpt .= "\nIgnoring files and directories: " . join ' ',  map { "'$_'" }
   @sIgnore if @sIgnore;
$sOpt .= "\nIgnoring packages: "   . join ' ',  map { "'$_'" } @sHide if @sHide;
print <<HERE;
Base Directory: $sDir0$sOpt
HERE

# Build file or directory name matcher
#    Adapted from Recipe 6.10 in Perl Cookbook
sub rfMatch {
   my ( $bDefault, $op, @sMask ) = @_; # @sMask must be a lexical array
   return sub { return $bDefault; } if 3 > @_;
   my $sExpr = join " $op\n",
      map {
         # Convert file expansion mask to regular expression
         $sMask[$_] =~ s/\./\\./g;
         $sMask[$_] =~ s/\?/.?/g;
         $sMask[$_] =~ s/\*/.*/g;
         "m:^\$sMask[$_]\$:oi";
         } 0 .. $#sMask;
   my $rfMatch = eval "sub { local \$_ = shift;\nreturn $sExpr; };";
   die $@ if $@;
   return $rfMatch;
}
local *bHide   = rfMatch( 0, '||', @sHide );
local *bIgnore = rfMatch( 0, '||', @sIgnore );

# Find and parse setup.ini files, collect other filenames at the same time
my ( %aSetup, $sRel, $sName, %sTarBall, %bDir );
my $wanted = sub {
   # Skip ., .., and files and directories in ignore list
   if ( '.' eq $_ || '..' eq $_ ) {
      $File::Find::prune = 1 if '..' eq $_;
      return;
   }
   if ( bIgnore( $_ ) ) {
      $File::Find::prune = 1 if -d $_; # Prune if a directory
      return;
   }

   # Handle directory or file
   $sRel = sRel( $File::Find::dir, $sDir0 );
   if ( -d $_ ) { $bDir{sRel( $File::Find::name, $sDir0 )} = 1; }
      # Remember directory name for possible removal
   elsif ( "setup.ini" eq $_ ) {
      # Parse setup.ini
      my @aSetup;

      # Get list of files to leave alone, includes subdirectory path
      # setup-timestamp: 1012849221
      # install: latest/bash/bash-2.05-1.tar.gz 576828
      # source:  latest/bash/bash-2.05-1-src.tar.gz 1792319
      my ( $bHide, $sGroup, $sType, $sFile, $sSize, $sVol, $sSubDir, $sName );
      my $sSetup = $File::Find::name;
      open( SETUP, $sSetup ) or usage( 'die', 1, "Can't open $sSetup, $!" );
      while ( <SETUP> ) {
         ( $sType, $sFile, $sSize )  = split /\s+/, $_;
         if    ( 'setup-timestamp:' eq $sType ) {
            $aSetup[0]         = $sFile; # Actually timestamp
         }
         elsif ( 'install:'         eq $sType ) {
            next if $bHide;
            ( $sVol, $sSubDir, $sName ) = File::Spec -> splitpath( $sFile );
            $sSubDir                    = File::Spec -> canonpath( $sSubDir );
            $aSetup[1]{$sName}          = [ $sGroup, $sSize, $sSubDir ];
         }
         elsif ( 'source:'          eq $sType ) {
            next if $bHide;
            ( $sVol, $sSubDir, $sName ) = File::Spec -> splitpath( $sFile );
            $sSubDir                    = File::Spec -> canonpath( $sSubDir );
            $aSetup[2]{$sName}          = [ $sGroup, $sSize, $sSubDir ];
         }
         elsif ( '[' eq substr( $_, 0, 1 ) )    { s/\s+$//; $sGroup = $_; } # ]
         elsif ( s/^@\s+// ) {
            s/\s+$//;
            $bHide  = bHide( $_ );
            $sGroup = '[curr]';
         }
      }
      close SETUP;
      usage( 'die', 1, "Nothing found in $sSetup" )
         if 3 != @aSetup || ! $aSetup[0];
      $aSetup{$sRel} = \@aSetup;
   }
   elsif ( ".tar.bz2" eq substr( $_, -8 ) || ".tar.gz" eq substr( $_, -7 ) ) {
      # Save name of archive file
      $sTarBall{$_}{File::Spec -> canonpath( $sRel )} = -s $_;
   }
   # Currently ignoring other types of files
};
find( $wanted, $sDir0 );
usage( "die", 1, "No setup.ini files found" ) if ! %aSetup;

# Pick newest setup.ini
my ( $tNewest, $sNewest, @sOldSetup ) = ( 0, "" );
foreach ( sort keys %aSetup ) {
   if ( $tNewest < $aSetup{$_}[0] ) {
      $tNewest = $aSetup{$_}[0];
      $sNewest = $_;
   }
}
print "Newest setup.ini files: ", scalar gmtime( $tNewest ), " GMT\n";
foreach ( sort keys %aSetup ) {
   print "   ", sUnPercent( sRel( $_ ) ), "\n" if $tNewest == $aSetup{$_}[0];
}

# Report or remove obsolete setup.ini files
foreach ( sort keys %aSetup ) {
   push @sOldSetup, $_ if $tNewest > $aSetup{$_}[0];
}
print "Obsolete setup.ini files:\n" if @sOldSetup;
foreach ( @sOldSetup ) {
   print "   ", scalar gmtime( $aSetup{$_}[0] ), " GMT ",
     sUnPercent( sRel( $_ ) ), "\n";
   unlink "$_/setup.ini" or print "      *** Can't remove, $!" if $bSetup;
}

# Check found files against those listed in latest setup.ini
my ( @sDir, $sDir, $sFile, @sDup, %sMove, @sRemove, @sWrongSize );
my %aInstall = ( %{$aSetup{$sNewest}[1]} );
my %aSource  = ( %{$aSetup{$sNewest}[2]} );
foreach $sName ( sort keys %sTarBall ) {
   @sDir  = sort keys %{$sTarBall{$sName}};
   $sDir  = $sDir[0];
   $sFile = File::Spec -> catfile( $sDir, $sName );
   if    ( 1 < @sDir ) {
      # More than one copy of a file was found
      push @sDup, "$sName: " . join( ", ", map { sUnPercent( $_ ) } @sDir );
   }
   elsif ( exists $aInstall{$sName} ) {
      if    ( $sDir eq $aInstall{$sName}[2] ) {} # Already in right place
      elsif ( $sTarBall{$sName}{$sDir} != $aInstall{$sName}[1] ) {
         # Wrong size
         push @sWrongSize,
            "$sFile: $sTarBall{$sName}{$sDir} != $aInstall{$sName}[1] in " .
            sUnPercent( $sDir );
      }
      else {
         $sMove{$sFile} = File::Spec -> catfile( $aInstall{$sName}[2], $sName );
      }
   }
   elsif ( exists $aSource{$sName} ) {
      if    ( $sDir eq $aSource{$sName}[2] ) {} # Already in right place
      elsif ( $sTarBall{$sName}{$sDir} != $aSource{$sName}[1] ) {
         # Wrong size
         push @sWrongSize,
            "$sFile: $sTarBall{$sName}{$sDir} != $aSource{$sName}[1] in " .
            sUnPercent( $sDir );
      }
      else {
         $sMove{$sFile} = File::Spec -> catfile( $aSource{$sName}[2], $sName );
      }
   }
   else {
      # File not in setup.ini
      push @sRemove, $sFile;
   }
}

# Check for missing files
my @sMissing = ();
my @sMissingList = ();

if ( $bInstall ) {
  foreach ( keys %aInstall ) {
	if ( ! exists $sTarBall{$_} &&
	  ( $bCurr || '[curr]' ne $aInstall{$_}[0] ) &&
	  ( $bPrev || '[prev]' ne $aInstall{$_}[0] ) &&
      ( $bTest || '[test]' ne $aInstall{$_}[0] ) &&
	  ( $bExp  || '[exp]'  ne $aInstall{$_}[0] ) ) {
		push @sMissing, "$aInstall{$_}[0] $aInstall{$_}[2]/$_" ;
		  # Alternative: $aInstall{$_}[0] . " " .
		  #              File::Spec -> catfile( $aInstall{$_}[2],  $_ ) }
		push @sMissingList, "$aInstall{$_}[2]/$_" ;
	  }
  }
}
if ( $bSource ) {
  foreach ( keys %aSource ) {
	if ( ! exists $sTarBall{$_} &&
	  ( $bCurr || '[curr]' ne $aSource{$_}[0] ) &&
      ( $bPrev || '[prev]' ne $aSource{$_}[0] ) &&
      ( $bTest || '[test]' ne $aSource{$_}[0] ) &&
      ( $bExp  || '[exp]'  ne $aSource{$_}[0] ) ) {
		push @sMissing, "$aSource{$_}[0] $_" ;
		  # Alternative: $aSource{$_}[0] . " " .
		  #              File::Spec -> catfile( $aSource{$_}[2],  $_ ) }
		push @sMissingList, "$aSource{$_}[2]/$_" ;
	  }
  }
}
@sMissing = sort @sMissing;

# Complain about errors
print join( "\n   ", "\nDuplicate Files",  @sDup ),       "\n" if @sDup;
print join( "\n   ", "\nWrong Size Files", @sWrongSize ), "\n" if @sWrongSize;
print join( "\n   ", "\nMissing Files:",   @sMissing ),   "\n" if @sMissing;

# Report list of missing files to disk
if ( $bWrite ) {
  @sMissingList = sort @sMissingList;
  open(REPORT, '>', 'missing.lst');
  print( REPORT join( "\n", @sMissingList ),"\n" ) if @sMissingList;
  close(REPORT);
}

# Move queued files to base directory tree
if ( %sMove ) {
   print $bMove ? "\n" : "\nNot ", "Moving files to base directory tree\n";
   my ( $sFrom, $sTo );
   foreach $sFrom ( sort keys %sMove ) {
      $sTo  = $sMove{$sFrom};
      $sDir = dirname( $sTo );
      print sUnPercent( "   $sFrom -> $sDir\n" );
      if ( -e $sTo ) { print "      *** Target already exists\n"; }
      elsif ( $bMove ) {
         mkpath( $sDir ) if ! -d $sDir;
         move( $sFrom, $sTo ) or print "      *** Can't move, $!\n";
      }
   }
}

# Remove files not listed in setup.ini
if ( @sRemove ) {
   print $bArch ? "\n" : "\nNot ", "Removing files not in setup.ini\n";
   foreach $sFile ( @sRemove ) {
      print sUnPercent( "   $sFile\n" );
      if ( $bArch ) {
         unlink( $sFile ) or print "      *** Can't remove, $!\n";
      }
   }
}

# Remove empty directories
my ( @g, $bFound );
foreach $sRel ( sort { length $b <=> length $a || $a cmp $b } keys %bDir ) {
   @g = glob( File::Spec -> catfile( $sRel, "*" ) );
   if ( ! @g ) {
      print $bArch ? "\n" : "\nNot ", "Removing Empty Directories\n"
         if ! $bFound++;
      print sUnPercent( "   $sRel\n" );
      if ( $bArch ) {
         rmdir $sRel or print "      *** Can't rmdir, $!\n";
      }
   }
}

# Copy latest setup.ini to base directory
if ( "." ne $sNewest ) {
   print sUnPercent( "\nCopying setup.ini from $sNewest to $sDir0\n" );
   copy( "$sNewest/setup.ini", "$sDir0/setup.ini" )
      or print "   *** Can't copy setup.ini, $!\n";
}

exit 0;

# Produce relative file or directory
sub sRel {
   my ( $sAbs, $sBase ) = @_;
   $sBase = "." if ! defined $sBase || ! length $sBase;
   my $sRel = File::Spec -> abs2rel( $sAbs, $sBase );
   $sRel =~ s,\\,/,g;
   $sRel =~ s/^\w://;
   $sRel =  "." if ! length $sRel;
   return $sRel;
}

# Convert %xx to characters
sub sUnPercent {
   local ( $_ ) = @_;

   s/\%([0-9a-f]{2})/chr(hex($1))/gie;
   return $_;
}

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Bug reporting:         http://cygwin.com/bugs.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]