| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # File::Repl | 
| 2 |  |  |  |  |  |  | # | 
| 3 |  |  |  |  |  |  | # Version | 
| 4 |  |  |  |  |  |  | #      $Source: C:/src/perl/File/Repl/RCS/Repl.pm $ | 
| 5 |  |  |  |  |  |  | #      $Revision: 2.3 $ | 
| 6 |  |  |  |  |  |  | #      $State: Exp $ | 
| 7 |  |  |  |  |  |  | # | 
| 8 |  |  |  |  |  |  | # Start comments/code here - will not be processed into manual pages | 
| 9 |  |  |  |  |  |  | # | 
| 10 |  |  |  |  |  |  | #    Copyright © Dave Roberts  2000,2001 | 
| 11 |  |  |  |  |  |  | # | 
| 12 |  |  |  |  |  |  | # Revision history: | 
| 13 |  |  |  |  |  |  | #      $Log: Repl.pm $ | 
| 14 |  |  |  |  |  |  | #      Revision 2.3  2015/11/03 18:07:45  Dave.Roberts | 
| 15 |  |  |  |  |  |  | #      interim release - corrections to help manage situations when a file and directory are compared | 
| 16 |  |  |  |  |  |  | # | 
| 17 |  |  |  |  |  |  | #      Revision 2.2  2015/11/01 22:36:38  Dave.Roberts | 
| 18 |  |  |  |  |  |  | #      removed Win32::Admin requirement from documentation | 
| 19 |  |  |  |  |  |  | # | 
| 20 |  |  |  |  |  |  | #      Revision 2.1  2015/07/15 20:51:29  Dave.Roberts | 
| 21 |  |  |  |  |  |  | #      added timestamp info for reading directories | 
| 22 |  |  |  |  |  |  | # | 
| 23 |  |  |  |  |  |  | #      Revision 2.0  2015/07/15 20:00:30  dave | 
| 24 |  |  |  |  |  |  | #      New major version, now with Win32::AdminMisc depandency removed as this | 
| 25 |  |  |  |  |  |  | #      module becomes more difficult to acquire and build for recent Perl releases | 
| 26 |  |  |  |  |  |  | # | 
| 27 |  |  |  |  |  |  | #      Revision 1.1  2015/07/15 19:58:06  Dave.Roberts | 
| 28 |  |  |  |  |  |  | #      Initial revision | 
| 29 |  |  |  |  |  |  | # | 
| 30 |  |  |  |  |  |  | #      Revision 1.31  2014/01/25 21:27:59  Dave.Roberts | 
| 31 |  |  |  |  |  |  | #      as advised from CPAN testing modified to include | 
| 32 |  |  |  |  |  |  | #      =encoding utf8 | 
| 33 |  |  |  |  |  |  | #      and | 
| 34 |  |  |  |  |  |  | #      escape the < and > characters in the pod with E and | 
| 35 |  |  |  |  |  |  | #      E respectively. | 
| 36 |  |  |  |  |  |  | # | 
| 37 |  |  |  |  |  |  | #      Revision 1.29  2010/05/04 15:02:05  Dave.Roberts | 
| 38 |  |  |  |  |  |  | #      corrected documentation - layout near Update method was incorrect | 
| 39 |  |  |  |  |  |  | # | 
| 40 |  |  |  |  |  |  | #      Revision 1.28  2010/04/27 14:55:00  Dave.Roberts | 
| 41 |  |  |  |  |  |  | #      minor code improvements in output messages for the Delete method | 
| 42 |  |  |  |  |  |  | # | 
| 43 |  |  |  |  |  |  | #      Revision 1.27  2010/04/13 08:36:52  Dave.Roberts | 
| 44 |  |  |  |  |  |  | #      added functionality for testing negative ages. This allows files older than the age | 
| 45 |  |  |  |  |  |  | #      specified to be selected (excluding all files younger) | 
| 46 |  |  |  |  |  |  | # | 
| 47 |  |  |  |  |  |  | #      Revision 1.26  2010/04/12 16:29:57  Dave.Roberts | 
| 48 |  |  |  |  |  |  | #      added Version method to return the File::Repl version | 
| 49 |  |  |  |  |  |  | #      corrected silly mistake in documentation - in definition of %con hash | 
| 50 |  |  |  |  |  |  | # | 
| 51 |  |  |  |  |  |  | #      Revision 1.25  2010/04/12 16:04:54  Dave.Roberts | 
| 52 |  |  |  |  |  |  | #      added example script for tombstoning | 
| 53 |  |  |  |  |  |  | #      removed windows linefeed characters from file | 
| 54 |  |  |  |  |  |  | # | 
| 55 |  |  |  |  |  |  | #      Revision 1.24  2010/04/07 02:00:11  Dave.Roberts | 
| 56 |  |  |  |  |  |  | #      modified code to remove the use of a hash as a reference - this was generating warnings | 
| 57 |  |  |  |  |  |  | #      as this use of a hash has beeen depreciated. | 
| 58 |  |  |  |  |  |  | # | 
| 59 |  |  |  |  |  |  | #      Revision 1.21  2002/02/07 10:37:39  Dave.Roberts | 
| 60 |  |  |  |  |  |  | #      corrected mode identified for Update method (the check used previously | 
| 61 |  |  |  |  |  |  | #      was invalid), and also synopsis for use of Update method (args incorrectly | 
| 62 |  |  |  |  |  |  | #      ordered) | 
| 63 |  |  |  |  |  |  | # | 
| 64 |  |  |  |  |  |  | #      Revision 1.20  2002/01/09 12:51:17  Dave.Roberts | 
| 65 |  |  |  |  |  |  | #      corrected errors in tombstoning of directories - subs $del and | 
| 66 |  |  |  |  |  |  | #      $deltree in particular | 
| 67 |  |  |  |  |  |  | # | 
| 68 |  |  |  |  |  |  | #      Revision 1.19  2001/11/21 21:28:19  Dave.Roberts | 
| 69 |  |  |  |  |  |  | #      resolved error in determining file age, especially when the 'a' file is | 
| 70 |  |  |  |  |  |  | #      missing | 
| 71 |  |  |  |  |  |  | #      evaluated the current time at start (set $runtime), and then removed | 
| 72 |  |  |  |  |  |  | #      many "time" calls | 
| 73 |  |  |  |  |  |  | # | 
| 74 |  |  |  |  |  |  | #      Revision 1.18  2001/08/22 07:10:41  Dave.Roberts | 
| 75 |  |  |  |  |  |  | #      logic change so that we don't use the Win32::API on win9x machines | 
| 76 |  |  |  |  |  |  | # | 
| 77 |  |  |  |  |  |  | #      Revision 1.17  2001/08/03 09:38:29  Dave.Roberts | 
| 78 |  |  |  |  |  |  | #      corrected code error (lines 572/3) where $$ was incorrectly used | 
| 79 |  |  |  |  |  |  | #      corrected code error (lines 572/3) where $$ was incorrectly used in truncation code | 
| 80 |  |  |  |  |  |  | # | 
| 81 |  |  |  |  |  |  | #      Revision 1.16  2001/08/02 22:09:02  Dave.Roberts | 
| 82 |  |  |  |  |  |  | #      corrected code for the Rename routine | 
| 83 |  |  |  |  |  |  | # | 
| 84 |  |  |  |  |  |  | #      Revision 1.15  2001/07/17 21:05:43  Dave.Roberts | 
| 85 |  |  |  |  |  |  | #      small changes to _arraysort - simplifying code | 
| 86 |  |  |  |  |  |  | # | 
| 87 |  |  |  |  |  |  | #      Revision 1.14  2001/07/12 21:51:50  jj768 | 
| 88 |  |  |  |  |  |  | #      additional documentation - and minor code changes | 
| 89 |  |  |  |  |  |  | # | 
| 90 |  |  |  |  |  |  | #      Revision 1.13  2001/07/12 15:18:43  Dave.Roberts | 
| 91 |  |  |  |  |  |  | #      code tidy up and reorganisation | 
| 92 |  |  |  |  |  |  | #      fixed logic errors (A>B! mode in Update method was not copying new files from A to B), also for A | 
| 93 |  |  |  |  |  |  | #      removed several local variables and used referred object directly | 
| 94 |  |  |  |  |  |  | # | 
| 95 |  |  |  |  |  |  | #      Revision 1.12  2001/07/11 10:30:16  Dave.Roberts | 
| 96 |  |  |  |  |  |  | #      resolved various errors introduced in 1.11 - mainly associsated with reference errors | 
| 97 |  |  |  |  |  |  | #      rehacked fc subroutine - to give more logical messages | 
| 98 |  |  |  |  |  |  | #      still in need of more documentation - esp of object reference returned and associated variables | 
| 99 |  |  |  |  |  |  | # | 
| 100 |  |  |  |  |  |  | #      Revision 1.11  2001/07/06 14:52:53  jj768 | 
| 101 |  |  |  |  |  |  | #      double referencing of blessed object removed (from New method) and subsequent | 
| 102 |  |  |  |  |  |  | #      methods updated. Requires Testing. | 
| 103 |  |  |  |  |  |  | #      Update and other methods now return reference to data arrays and hashs evaluated | 
| 104 |  |  |  |  |  |  | #      during method call | 
| 105 |  |  |  |  |  |  | # | 
| 106 |  |  |  |  |  |  | #      Revision 1.10  2001/07/06 08:23:48  Dave.Roberts | 
| 107 |  |  |  |  |  |  | #      code changes to allow the colume info to be detected correctly using Win32::AdminMisc | 
| 108 |  |  |  |  |  |  | #      when a drive letter is specified (was only working with UNC names) | 
| 109 |  |  |  |  |  |  | # | 
| 110 |  |  |  |  |  |  | #      Revision 1.9  2001/06/27 13:35:53  Dave.Roberts | 
| 111 |  |  |  |  |  |  | #      minor presentation changes | 
| 112 |  |  |  |  |  |  | # | 
| 113 |  |  |  |  |  |  | #      Revision 1.8  2001/06/27 12:59:22  jj768 | 
| 114 |  |  |  |  |  |  | #      logic to prevent "Use of uninitialized value in pattern match (m//)" errors on use of $vol{FileSystemName} | 
| 115 |  |  |  |  |  |  | # | 
| 116 |  |  |  |  |  |  | #      Revision 1.6  2001/06/21 12:32:15  jj768 | 
| 117 |  |  |  |  |  |  | #      *** empty log message *** | 
| 118 |  |  |  |  |  |  | # | 
| 119 |  |  |  |  |  |  | #      Revision 1.5  2001/06/20 20:39:21  Dave.Roberts | 
| 120 |  |  |  |  |  |  | #      minor header changes | 
| 121 |  |  |  |  |  |  | # | 
| 122 |  |  |  |  |  |  | #      Revision 1.4  2001/06/20 19:55:21  jj768 | 
| 123 |  |  |  |  |  |  | #      re-built module source files as per perlmodnew manpage | 
| 124 |  |  |  |  |  |  | # | 
| 125 |  |  |  |  |  |  | # | 
| 126 |  |  |  |  |  |  | #****************************************************************************** | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | package File::Repl; | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | require 5.005_62; | 
| 131 | 1 |  |  | 1 |  | 10045 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 25 |  | 
| 132 | 1 |  |  | 1 |  | 5 | use warnings; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 35 |  | 
| 133 | 1 |  |  | 1 |  | 6 | use Carp; | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 1 |  |  |  |  | 73 |  | 
| 134 | 1 |  |  | 1 |  | 6 | use File::Find; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 61 |  | 
| 135 | 1 |  |  | 1 |  | 809 | use File::Copy; | 
|  | 1 |  |  |  |  | 5582 |  | 
|  | 1 |  |  |  |  | 68 |  | 
| 136 | 1 |  |  | 1 |  | 7 | use File::Basename; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 195 |  | 
| 137 | 1 |  |  | 1 |  | 6 | use constant FALSE                => 0; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 72 |  | 
| 138 | 1 |  |  | 1 |  | 5 | use constant TRUE                 => 1; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 49 |  | 
| 139 | 1 |  |  | 1 |  | 6 | use constant TIME_ZONE_ID_INVALID => 0xFFFFFFFF; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 8393 |  | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | my($runtime) = time; | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | #************************************************************** | 
| 145 |  |  |  |  |  |  | # On FAT filesystems, "stat" adds TZ_BIAS to the actual file | 
| 146 |  |  |  |  |  |  | # times (atime, ctime and mtime) and "utime" subtracts TZ_BIAS | 
| 147 |  |  |  |  |  |  | # from the supplied parameters before setting file times.  To | 
| 148 |  |  |  |  |  |  | # maintain FAT at UTC time, we need to do the opposite. | 
| 149 |  |  |  |  |  |  | # | 
| 150 |  |  |  |  |  |  | # If we don't maintain FAT filesystems at UTC time and the repl | 
| 151 |  |  |  |  |  |  | # is between FAT and NON-FAT systems, then all files will get | 
| 152 |  |  |  |  |  |  | # replicated whenever the TZ or Daylight Savings Time changes. | 
| 153 |  |  |  |  |  |  | # | 
| 154 |  |  |  |  |  |  | # (NH270301) | 
| 155 |  |  |  |  |  |  | # | 
| 156 |  |  |  |  |  |  | my $TZ_BIAS = 0;               # global package variable | 
| 157 |  |  |  |  |  |  | if ($^O eq 'MSWin32') {        # is this a win32 system ? | 
| 158 |  |  |  |  |  |  | if ( eval "use Win32" ) { | 
| 159 |  |  |  |  |  |  | my($string,$major,$minor,$build,$id) = Win32::GetOSVersion(); | 
| 160 |  |  |  |  |  |  | if ( $id == 2 ) {   #  Machine is NT (0=Win32s, 1=Win9x etc) | 
| 161 |  |  |  |  |  |  | eval "use Win32::API"; | 
| 162 |  |  |  |  |  |  | my $lpTimeZoneInformation = "\0" x 172;   # space for struct _TIME_ZONE_INFORMATION | 
| 163 |  |  |  |  |  |  | my $GetTimeZoneInformation = new Win32::API("kernel32", 'GetTimeZoneInformation', ['P'], 'N'); | 
| 164 |  |  |  |  |  |  | croak "\n ERROR: failed to import GetTimeZoneInformation API function\n" if !$GetTimeZoneInformation; | 
| 165 |  |  |  |  |  |  | my $ISDST = $GetTimeZoneInformation->Call($lpTimeZoneInformation); | 
| 166 |  |  |  |  |  |  | croak "\n ERROR: GetTimeZoneInformation returned invalid data: " . Win32::FormatMessage(Win32::GetLastError()) | 
| 167 |  |  |  |  |  |  | if $ISDST == TIME_ZONE_ID_INVALID; | 
| 168 |  |  |  |  |  |  | my ($Bias,$StandardBias,$DaylightBias) = unpack "l x80 l x80 l", $lpTimeZoneInformation; | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | # $ISDST == 0 -  No Daylight Savings in this timezone (no transition dates defined for this tz) | 
| 171 |  |  |  |  |  |  | # $ISDST == 1 -  Standard time | 
| 172 |  |  |  |  |  |  | # $ISDST == 2 -  Daylight Savings time | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | # bias times are returned in minutes - convert to seconds | 
| 175 |  |  |  |  |  |  | $TZ_BIAS = ($Bias + ($ISDST == 0 ? 0 : ($ISDST == 2 ? $DaylightBias : $StandardBias))) * 60; | 
| 176 |  |  |  |  |  |  | } | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  | } | 
| 179 |  |  |  |  |  |  | #************************************************************** | 
| 180 |  |  |  |  |  |  | require Exporter; | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | our @ISA = qw(Exporter); | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | # Items to export into callers namespace by default. Note: do not export | 
| 185 |  |  |  |  |  |  | # names by default without a very good reason. Use EXPORT_OK instead. | 
| 186 |  |  |  |  |  |  | # Do not simply export all your public functions/methods/constants. | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | # This allows declaration		 use File::Repl ':all'; | 
| 189 |  |  |  |  |  |  | # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK | 
| 190 |  |  |  |  |  |  | # will save memory. | 
| 191 |  |  |  |  |  |  | our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); | 
| 192 |  |  |  |  |  |  | our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); | 
| 193 |  |  |  |  |  |  | our @EXPORT = qw( | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | ); | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | our $VERSION = sprintf("%d.%d", q$Revision: 2.3 $ =~ /(\d+)\.(\d+)/); | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | # Preloaded methods go here. | 
| 200 |  |  |  |  |  |  | #--------------------------------------------------------------------- | 
| 201 |  |  |  |  |  |  | sub New { | 
| 202 | 10 |  |  | 10 | 1 | 21292 | my $class = shift; | 
| 203 | 10 |  |  |  |  | 22 | my($conf) = $_[0]; | 
| 204 | 10 | 50 |  |  |  | 35 | croak "\n Usage: File::Repl->New(\$hashref)\n\n" unless (ref($conf) eq "HASH"); | 
| 205 | 10 |  |  |  |  | 13 | my($alist,$blist,$atype,$btype,$key,$xxx,$dira,$dirb,$tmp); | 
| 206 | 10 |  |  |  |  | 30 | $conf->{dira} =~ s/\\/\//g;     # Make dir use forward slash's | 
| 207 | 10 |  |  |  |  | 16 | $conf->{dirb} =~ s/\\/\//g;     # Make dir use forward slash's | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | # To maintain backwards compatibility, check if additional | 
| 210 |  |  |  |  |  |  | # keys are defined and default to a suitable value (NH271100) | 
| 211 |  |  |  |  |  |  | my $r_con = { | 
| 212 |  |  |  |  |  |  | dira     => $conf->{dira}, | 
| 213 |  |  |  |  |  |  | dirb     => $conf->{dirb}, | 
| 214 |  |  |  |  |  |  | verbose  => (defined $conf->{verbose}) ? $conf->{verbose} : 0,       # default not verbose | 
| 215 |  |  |  |  |  |  | agelimit => (defined $conf->{age})     ? $conf->{age}     : 0,       # default 0 (don't check age) | 
| 216 |  |  |  |  |  |  | ttl      => (defined $conf->{ttl})     ? $conf->{ttl}     : 31,      # default ttl 31 days | 
| 217 |  |  |  |  |  |  | nocase   => (defined $conf->{nocase})  ? $conf->{nocase}  : TRUE,    # default nocase TRUE | 
| 218 |  |  |  |  |  |  | bmark    => (defined $conf->{bmark})   ? $conf->{bmark}   : FALSE,   # default benchmark FALSE | 
| 219 |  |  |  |  |  |  | recurse  => (defined $conf->{recurse}) ? $conf->{recurse} : TRUE,    # default recurse TRUE | 
| 220 | 10 | 100 |  |  |  | 184 | mkdirs   => (defined $conf->{mkdirs})  ? $conf->{mkdirs}  : FALSE,   # default mkdirs FALSE | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | }; | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | # Should we continue if dira / dirb dosn't exist ?  (NH301200) | 
| 224 | 10 | 100 |  |  |  | 30 | if ( $r_con->{verbose} >= 3 ) { | 
| 225 | 1 |  |  |  |  | 23 | printf "\n\nFile:Repl configuration settings:\n"; | 
| 226 | 1 |  |  |  |  | 19 | printf "-------------------------------\n"; | 
| 227 | 1 |  |  |  |  | 6 | foreach $key (keys %$r_con ) { | 
| 228 | 9 |  |  |  |  | 175 | printf "  Key %-10s   Value %-30s\n",$key, $r_con->{$key}; | 
| 229 |  |  |  |  |  |  | } | 
| 230 |  |  |  |  |  |  | } | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | # Build the A list | 
| 233 | 10 | 50 |  |  |  | 30 | benchmark("init") if $r_con->{bmark}; | 
| 234 | 10 | 50 |  |  |  | 160 | if ( -d $r_con->{dira} ) { | 
|  |  | 0 |  |  |  |  |  | 
| 235 | 10 |  |  |  |  | 257 | printf "\treading dira %s\n",$r_con->{dira}; | 
| 236 | 10 |  |  |  |  | 31 | my($start_dir) = time; | 
| 237 | 10 | 50 |  |  |  | 32 | if ($r_con->{recurse}) { | 
| 238 |  |  |  |  |  |  | $xxx = sub{ | 
| 239 | 100 |  |  | 100 |  | 504 | ($tmp = $File::Find::name) =~ s/^\Q$r_con->{dira}//;        # Remove the start directory portion | 
| 240 | 100 | 100 |  |  |  | 4316 | ($atype->{$tmp}, $alist->{$tmp}) = (stat($_))[2,9] if $tmp; # Mode is 3rd element, mtime is 10th | 
| 241 | 10 |  |  |  |  | 60 | }; | 
| 242 | 10 |  |  |  |  | 648 | find(\&$xxx,$r_con->{dira}); | 
| 243 |  |  |  |  |  |  | }else{ | 
| 244 | 0 | 0 |  |  |  | 0 | opendir(DIRA, "$r_con->{dira}") || croak "Can not open $r_con->{dira} directory !!!\n"; | 
| 245 | 0 |  |  |  |  | 0 | while($tmp = readdir(DIRA)) { | 
| 246 | 0 |  |  |  |  | 0 | $tmp = "/" . $tmp; | 
| 247 | 0 | 0 |  |  |  | 0 | next if -d $r_con->{dira} . $tmp;                           # Skip directories | 
| 248 | 0 |  |  |  |  | 0 | ($atype->{$tmp}, $alist->{$tmp}) = (stat($r_con->{dira} . $tmp))[2,9]; | 
| 249 |  |  |  |  |  |  | } | 
| 250 | 0 |  |  |  |  | 0 | close DIRA; | 
| 251 |  |  |  |  |  |  | } | 
| 252 | 10 |  |  |  |  | 70 | printf "\tcompleted reading dira (%s sec)\n",&elapsed($start_dir); | 
| 253 |  |  |  |  |  |  | }elsif (!$r_con->{mkdirs}) { | 
| 254 | 0 |  |  |  |  | 0 | croak "Invalid directory name for dira ($r_con->{dira})\n"; | 
| 255 |  |  |  |  |  |  | } | 
| 256 | 10 | 50 |  |  |  | 42 | benchmark("build A list") if $r_con->{bmark}; | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | # Build the B list | 
| 259 | 10 | 50 |  |  |  | 23 | benchmark("init") if $r_con->{bmark}; | 
| 260 | 10 | 100 |  |  |  | 166 | if ( $r_con->{dira} eq $r_con->{dirb} ) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 261 | 1 |  |  |  |  | 3 | $blist = $alist; | 
| 262 | 1 |  |  |  |  | 3 | $btype = $atype; | 
| 263 |  |  |  |  |  |  | }elsif ( -d $r_con->{dirb} ) { | 
| 264 | 9 |  |  |  |  | 177 | printf "\treading dirb %s\n",$r_con->{dirb}; | 
| 265 | 9 |  |  |  |  | 27 | my($start_dir) = time; | 
| 266 | 9 | 50 |  |  |  | 21 | if ($r_con->{recurse}) { | 
| 267 |  |  |  |  |  |  | $xxx = sub{ | 
| 268 | 59 |  |  | 59 |  | 299 | ($tmp = $File::Find::name) =~ s/^\Q$r_con->{dirb}//;        # Remove the start directory portion | 
| 269 | 59 | 100 |  |  |  | 2680 | ($btype->{$tmp}, $blist->{$tmp}) = (stat($_))[2,9] if $tmp; # Mode is 3rd element, mtime is 10th | 
| 270 | 9 |  |  |  |  | 46 | }; | 
| 271 | 9 |  |  |  |  | 504 | find(\&$xxx,$r_con->{dirb}); | 
| 272 |  |  |  |  |  |  | }else{ | 
| 273 | 0 | 0 |  |  |  | 0 | opendir(DIRB, "$r_con->{dirb}") || croak "Can not open $r_con->{dirb} directory !!!\n"; | 
| 274 | 0 |  |  |  |  | 0 | while($tmp = readdir(DIRB)) { | 
| 275 | 0 |  |  |  |  | 0 | $tmp = "/" . $tmp; | 
| 276 | 0 | 0 |  |  |  | 0 | next if -d $r_con->{dirb} . $tmp;                           # Skip directories | 
| 277 | 0 |  |  |  |  | 0 | ($btype->{$tmp}, $blist->{$tmp}) = (stat($r_con->{dirb} . $tmp))[2,9]; | 
| 278 |  |  |  |  |  |  | } | 
| 279 | 0 |  |  |  |  | 0 | close DIRB; | 
| 280 |  |  |  |  |  |  | } | 
| 281 | 9 |  |  |  |  | 32 | printf "\tcompleted reading dirb (%s sec)\n",&elapsed($start_dir); | 
| 282 |  |  |  |  |  |  | }elsif (!$r_con->{mkdirs}) { | 
| 283 | 0 |  |  |  |  | 0 | croak "Invalid directory name for dirb ($r_con->{dirb})\n"; | 
| 284 |  |  |  |  |  |  | } | 
| 285 | 10 | 50 |  |  |  | 42 | benchmark("build B list") if $r_con->{bmark}; | 
| 286 | 10 |  |  |  |  | 20 | $r_con->{alist} = $alist; | 
| 287 | 10 |  |  |  |  | 17 | $r_con->{atype} = $atype; | 
| 288 | 10 |  |  |  |  | 14 | $r_con->{blist} = $blist; | 
| 289 | 10 |  |  |  |  | 16 | $r_con->{btype} = $btype; | 
| 290 | 10 |  |  |  |  | 33 | bless  $r_con, $class; | 
| 291 | 10 |  |  |  |  | 66 | return $r_con; | 
| 292 |  |  |  |  |  |  | } | 
| 293 |  |  |  |  |  |  | #===================================================================== | 
| 294 |  |  |  |  |  |  | sub Update { | 
| 295 | 8 |  |  | 8 | 1 | 54 | return _generic ("Update",@_); | 
| 296 |  |  |  |  |  |  | } | 
| 297 |  |  |  |  |  |  | #===================================================================== | 
| 298 |  |  |  |  |  |  | sub Rename { | 
| 299 | 0 |  |  | 0 | 1 | 0 | return _generic ("Rename",@_); | 
| 300 |  |  |  |  |  |  | } | 
| 301 |  |  |  |  |  |  | #===================================================================== | 
| 302 |  |  |  |  |  |  | sub Version { | 
| 303 | 1 |  |  | 1 | 1 | 72 | return $VERSION; | 
| 304 |  |  |  |  |  |  | } | 
| 305 |  |  |  |  |  |  | #===================================================================== | 
| 306 |  |  |  |  |  |  | sub Process { | 
| 307 | 0 | 0 |  | 0 | 1 | 0 | if ( scalar(@_) eq 3 ) { | 
|  |  | 0 |  |  |  |  |  | 
| 308 | 0 |  |  |  |  | 0 | my($r_con,$regex,$sub) =@_; | 
| 309 | 0 |  |  |  |  | 0 | my($negregex) = '^$';   # Make this impossible to match, nor file or directory | 
| 310 |  |  |  |  |  |  | # can be of zero length name. | 
| 311 |  |  |  |  |  |  | }elsif ( scalar(@_) eq 4 ) { | 
| 312 | 0 |  |  |  |  | 0 | my($r_con,$regex,$negregex,$sub) =@_; | 
| 313 |  |  |  |  |  |  | }else{ | 
| 314 | 0 |  |  |  |  | 0 | carp ("Try calling the File::Repl->Process method with the right arguments !\n"); | 
| 315 |  |  |  |  |  |  | } | 
| 316 | 0 |  |  |  |  | 0 | print "The Process method is not implemented\n"; | 
| 317 |  |  |  |  |  |  | } | 
| 318 |  |  |  |  |  |  | #===================================================================== | 
| 319 |  |  |  |  |  |  | sub Compress { | 
| 320 | 0 | 0 |  | 0 | 1 | 0 | if ( scalar(@_) eq 3 ) { | 
|  |  | 0 |  |  |  |  |  | 
| 321 | 0 |  |  |  |  | 0 | my($r_con,$regex,$archive) =@_; | 
| 322 | 0 |  |  |  |  | 0 | my($negregex) = '^$';   # Make this impossible to match, nor file or directory | 
| 323 |  |  |  |  |  |  | # can be of zero length name. | 
| 324 |  |  |  |  |  |  | }elsif ( scalar(@_) eq 4 ) { | 
| 325 | 0 |  |  |  |  | 0 | my($r_con,$regex,$negregex,$mode,$commit) =@_; | 
| 326 |  |  |  |  |  |  | }else{ | 
| 327 | 0 |  |  |  |  | 0 | carp ("Try calling the File::Repl->Compress method with the right arguments !\n"); | 
| 328 |  |  |  |  |  |  | } | 
| 329 | 0 |  |  |  |  | 0 | print "The Compress method is not implemented\n"; | 
| 330 |  |  |  |  |  |  | } | 
| 331 |  |  |  |  |  |  | #===================================================================== | 
| 332 |  |  |  |  |  |  | sub Delete { | 
| 333 | 1 |  |  | 1 | 1 | 7 | return _generic ("Delete",@_); | 
| 334 |  |  |  |  |  |  | } | 
| 335 |  |  |  |  |  |  | #===================================================================== | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  | sub _generic { | 
| 338 | 9 |  |  | 9 |  | 20 | my ($caller) = shift @_; | 
| 339 | 9 |  |  |  |  | 14 | my($r_con,$regex,$mode,$commit,$nsub); | 
| 340 | 0 |  |  |  |  | 0 | my($refa,$refb,$refatype,$refbtype,$agelimit,$verbose); | 
| 341 | 0 |  |  |  |  | 0 | my($name,$mtime,%mark,$afile,$bfile,$amtime,$bmtime,$fc,$md,$del,$type); | 
| 342 | 0 |  |  |  |  | 0 | my(@amatch,@bmatch,$benchmark,$tfiles,$common,$aonly,$bonly,$amatch,$bmatch,@temp,%vol); | 
| 343 | 0 |  |  |  |  | 0 | my($tName,$aName,$bName,$deltree,$truncate,$touch,$mv,$tmp,$atype,$btype); | 
| 344 | 9 |  |  |  |  | 13 | my ($negregex) = '^$';# Default value - make this impossible to match, neither file nor directory | 
| 345 | 9 |  |  |  |  | 13 | my $tz_bias_a = 0; | 
| 346 | 9 |  |  |  |  | 12 | my $tz_bias_b = 0; | 
| 347 | 9 |  |  |  |  | 10 | my $fudge = 2;  # Fudge factor to allow two machines to synch via a removeable drive/disc (A <> DOS <> B) | 
| 348 |  |  |  |  |  |  |  | 
| 349 | 9 | 100 |  |  |  | 24 | if ($caller eq "Update") { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 350 | 8 | 50 |  |  |  | 15 | if ( scalar(@_) == 4 ) { | 
|  |  | 0 |  |  |  |  |  | 
| 351 | 8 |  |  |  |  | 22 | ($r_con,$regex,$mode,$commit) = @_; | 
| 352 |  |  |  |  |  |  | }elsif ( scalar(@_) == 5 ) { | 
| 353 | 0 |  |  |  |  | 0 | ($r_con,$regex,$negregex,$mode,$commit) = @_; | 
| 354 |  |  |  |  |  |  | }else{ | 
| 355 | 0 |  |  |  |  | 0 | carp ("Call the Update method with the right arguments !\n\t\$ref->Update(regex, [noregex,] action, commit)"); | 
| 356 | 0 |  |  |  |  | 0 | print scalar(@_), " Args called ( @_ )\n"; | 
| 357 | 0 |  |  |  |  | 0 | return; | 
| 358 |  |  |  |  |  |  | } | 
| 359 | 8 | 50 |  |  |  | 23 | if ( $mode eq "" ) {      # Set the default operating mode | 
| 360 | 0 |  |  |  |  | 0 | $mode      = 'a>b'; | 
| 361 | 0 | 0 |  |  |  | 0 | print "using default mode for Update method (a>b)\n" if ($verbose > 1); | 
| 362 |  |  |  |  |  |  | } | 
| 363 | 8 | 50 |  |  |  | 40 | if ( $mode !~ /^(A>B!?)|(A<>B)|(A]b)|(a<>b)$/ ) { | 
| 364 | 0 |  |  |  |  | 0 | carp("Illegal mode used for Update method - legal options are\n\tA>B\tA>B!\tAB\tab\ta<>b\n"); | 
| 365 | 0 |  |  |  |  | 0 | return; | 
| 366 |  |  |  |  |  |  | } | 
| 367 |  |  |  |  |  |  | }elsif($caller eq "Delete"){ | 
| 368 | 1 | 50 |  |  |  | 7 | if ( scalar(@_) eq 3 ) { | 
|  |  | 0 |  |  |  |  |  | 
| 369 | 1 |  |  |  |  | 3 | ($r_con,$regex,$commit) =@_; | 
| 370 |  |  |  |  |  |  | }elsif ( scalar(@_) eq 4 ) { | 
| 371 | 0 |  |  |  |  | 0 | ($r_con,$regex,$negregex,$commit) =@_; | 
| 372 |  |  |  |  |  |  | }else{ | 
| 373 | 0 |  |  |  |  | 0 | carp ("Call the Delete method with the right arguments !\n\t\$ref->Delete(regex, [noregex], commit)"); | 
| 374 |  |  |  |  |  |  | } | 
| 375 |  |  |  |  |  |  | }elsif($caller eq "Rename"){ | 
| 376 | 0 | 0 |  |  |  | 0 | if ( scalar(@_) eq 4 ) { | 
|  |  | 0 |  |  |  |  |  | 
| 377 | 0 |  |  |  |  | 0 | ($r_con,$regex,$nsub,$commit) =@_; | 
| 378 |  |  |  |  |  |  | }elsif ( scalar(@_) eq 5 ) { | 
| 379 | 0 |  |  |  |  | 0 | ($r_con,$regex,$negregex,$nsub,$commit) =@_; | 
| 380 |  |  |  |  |  |  | }else{ | 
| 381 | 0 |  |  |  |  | 0 | carp ("Call the Rename method with the right arguments !\n\t\$ref->Rename(regex, [noregex], namesub, commit)"); | 
| 382 |  |  |  |  |  |  | } | 
| 383 |  |  |  |  |  |  | } | 
| 384 |  |  |  |  |  |  |  | 
| 385 | 9 |  |  |  |  | 19 | my $ttl = $r_con->{ttl} * 86400;                               # Expiry time for tombstone indicator files in seconds | 
| 386 | 9 | 50 |  |  |  | 20 | $commit    = TRUE unless defined $commit;                      # Set default commit value | 
| 387 | 9 |  |  |  |  | 13 | $verbose   = $r_con->{verbose}; | 
| 388 | 9 | 100 |  |  |  | 23 | $agelimit  = $r_con->{agelimit} ? $r_con->{agelimit} * 86400 : 0; # Determine age limit in seconds | 
| 389 | 9 | 50 |  |  |  | 20 | $negregex  = '^$' unless $negregex;                            # Ensure no matches if $negregex = '' | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | # Fix for stat/utime on FAT filesystems (NH270301) | 
| 393 | 9 | 50 |  |  |  | 22 | if ($TZ_BIAS) { | 
| 394 | 0 | 0 | 0 |  |  | 0 | if ( ( $r_con->{dira} =~ /^([a-z]:)/i ) ||                   # First match a drive letter - ie D: | 
|  |  |  | 0 |  |  |  |  | 
| 395 |  |  |  |  |  |  | ( $r_con->{dira} =~ /^([\\\/].\w+[\\\/][a-z0-9\$]+)/i ) ||   # Else match a share - ie //comp/share or \\comp\share | 
| 396 |  |  |  |  |  |  | ( Win32::GetCwd() =~ /^([a-z]:)/i ) ) {                      # Else assume relative path - use CWD | 
| 397 | 0 | 0 |  |  |  | 0 | $tz_bias_a = $TZ_BIAS if (FsType(1) =~ m/FAT/); | 
| 398 |  |  |  |  |  |  | } | 
| 399 | 0 | 0 | 0 |  |  | 0 | if ( ( $r_con->{dirb} =~ /^([a-z]:)/i ) ||                   # First match a drive letter - ie D: | 
|  |  |  | 0 |  |  |  |  | 
| 400 |  |  |  |  |  |  | ( $r_con->{dirb} =~ /^([\\\/].\w+[\\\/][a-z0-9\$]+)/i ) ||   # Else match a share - ie //comp/share or \\comp\share | 
| 401 |  |  |  |  |  |  | ( Win32::GetCwd() =~ /^([a-z]:)/i ) ) {                      # Else assume relative path - use CWD | 
| 402 | 0 | 0 |  |  |  | 0 | $tz_bias_b = $TZ_BIAS if (FsType($1) =~ m/FAT/); | 
| 403 |  |  |  |  |  |  | } | 
| 404 | 0 | 0 | 0 |  |  | 0 | $tz_bias_a = $tz_bias_b = 0 if ($tz_bias_a && $tz_bias_b); | 
| 405 |  |  |  |  |  |  | } | 
| 406 | 9 | 100 |  |  |  | 22 | if ($caller eq "Update") { | 
|  |  | 50 |  |  |  |  |  | 
| 407 | 8 | 100 |  |  |  | 49 | print "Update | 
| 408 |  |  |  |  |  |  | Regex    : $regex | 
| 409 |  |  |  |  |  |  | NegRegex : $negregex | 
| 410 |  |  |  |  |  |  | Mode     : $mode | 
| 411 |  |  |  |  |  |  | Commit   : $commit | 
| 412 |  |  |  |  |  |  | AgeLimit : $r_con->{agelimit} days ($agelimit seconds) | 
| 413 |  |  |  |  |  |  | Tombstone File TTL : $ttl | 
| 414 |  |  |  |  |  |  | DirA DOS time adj  : $tz_bias_a | 
| 415 |  |  |  |  |  |  | DirB DOS time adj  : $tz_bias_b\n\n" if ($verbose >= 3); | 
| 416 |  |  |  |  |  |  | }elsif ($caller eq "Delete"){ | 
| 417 | 1 | 50 |  |  |  | 4 | print "Delete | 
| 418 |  |  |  |  |  |  | Regex    : $regex | 
| 419 |  |  |  |  |  |  | NegRegex : $negregex | 
| 420 |  |  |  |  |  |  | Commit   : $commit | 
| 421 |  |  |  |  |  |  | AgeLimit : $r_con->{agelimit} days ($agelimit seconds) | 
| 422 |  |  |  |  |  |  | Tombstone File TTL : $ttl | 
| 423 |  |  |  |  |  |  | DirA DOS time adj  : $tz_bias_a\n\n" if ($verbose >= 3); | 
| 424 |  |  |  |  |  |  | } | 
| 425 |  |  |  |  |  |  | # Sort files using regex and negregex | 
| 426 | 9 | 50 |  |  |  | 19 | benchmark("init") if $r_con->{bmark}; | 
| 427 |  |  |  |  |  |  | ($tfiles,$common,$aonly,$bonly,$amatch,$bmatch) = | 
| 428 | 9 |  |  |  |  | 27 | _arraysort($r_con, $regex, $negregex, $r_con->{nocase}) ; | 
| 429 | 9 | 50 |  |  |  | 28 | benchmark("match files") if $r_con->{bmark}; | 
| 430 | 9 |  |  |  |  | 15 | $refa     = $r_con->{alist}; | 
| 431 | 9 |  |  |  |  | 12 | $refatype = $r_con->{atype}; | 
| 432 | 9 |  |  |  |  | 15 | $refb     = $r_con->{blist}; | 
| 433 | 9 |  |  |  |  | 13 | $refbtype = $r_con->{btype}; | 
| 434 |  |  |  |  |  |  | #**************************************************************** | 
| 435 |  |  |  |  |  |  | # sub to copy files and build directory structure | 
| 436 |  |  |  |  |  |  | #**************************************************************** | 
| 437 |  |  |  |  |  |  | $fc = sub { | 
| 438 | 24 |  |  | 24 |  | 40 | my($a,$b,$amtime,$bmtime,$disp,$mode) = @_; | 
| 439 | 24 |  |  |  |  | 30 | my($A,$B,$Amtime,$Bmtime,$Btmp,$age,$msg); | 
| 440 | 24 | 100 |  |  |  | 236 | print "fc ($a,$b,$amtime,$bmtime,$disp,$mode)\n" if $verbose > 3; | 
| 441 | 24 | 50 |  |  |  | 59 | if ( $disp eq "-->" ) { | 
|  |  | 0 |  |  |  |  |  | 
| 442 | 24 |  |  |  |  | 27 | $A = $a; | 
| 443 | 24 |  |  |  |  | 26 | $B = $b; | 
| 444 | 24 |  |  |  |  | 33 | $Amtime = $amtime; | 
| 445 | 24 |  |  |  |  | 32 | $Bmtime = $bmtime; | 
| 446 |  |  |  |  |  |  | }elsif( $disp eq "<--" ) { | 
| 447 | 0 |  |  |  |  | 0 | $A = $b; | 
| 448 | 0 |  |  |  |  | 0 | $B = $a; | 
| 449 | 0 |  |  |  |  | 0 | $Amtime = $bmtime; | 
| 450 | 0 |  |  |  |  | 0 | $Bmtime = $amtime; | 
| 451 |  |  |  |  |  |  | }else{ | 
| 452 | 0 |  |  |  |  | 0 | print "Illegal display option called ($disp)\n"; | 
| 453 | 0 |  |  |  |  | 0 | return 0; | 
| 454 |  |  |  |  |  |  | } | 
| 455 |  |  |  |  |  |  | # so we are always copying A to B | 
| 456 | 24 |  |  |  |  | 61 | $msg = " $a $disp $b"; | 
| 457 | 24 | 50 |  |  |  | 52 | if ( $amtime == 0 ) { | 
|  |  | 100 |  |  |  |  |  | 
| 458 | 0 |  |  |  |  | 0 | $age = $bmtime; | 
| 459 |  |  |  |  |  |  | }elsif ( $bmtime == 0 ) { | 
| 460 | 22 |  |  |  |  | 25 | $age = $amtime; | 
| 461 |  |  |  |  |  |  | }else{ | 
| 462 | 2 | 50 |  |  |  | 6 | ($bmtime > $amtime) ? $age = $bmtime : $age = $amtime; # Find the most recent mtime ($age) | 
| 463 |  |  |  |  |  |  | } | 
| 464 | 24 | 100 |  |  |  | 47 | if ( $agelimit ){ | 
| 465 | 20 | 50 |  |  |  | 32 | if ($agelimit > 0) {   # if $agelimit is positive - ignore files older than the agelimit | 
| 466 | 20 | 50 |  |  |  | 48 | if ( ( $runtime - $age ) > $agelimit ) { # Test for agelimit exceeded | 
| 467 |  |  |  |  |  |  | #print "amtime $amtime\nbmtime $bmtime\nage    $age\n"; | 
| 468 | 0 | 0 |  |  |  | 0 | printf "%s - exceeds age limit (%3.1d days old - limit is set to less than %3d days)\n",$msg,(time - $age)/86400,$agelimit/86400 if ($verbose > 1); | 
| 469 | 0 |  |  |  |  | 0 | return FALSE; | 
| 470 |  |  |  |  |  |  | } | 
| 471 |  |  |  |  |  |  | }else{                 # agelimit is negative - ignore files newer than the agelimit | 
| 472 | 0 | 0 |  |  |  | 0 | if ( ( $runtime - $age ) > -$agelimit ) { # Test for agelimit exceeded | 
| 473 |  |  |  |  |  |  | #print "amtime $amtime\nbmtime $bmtime\nage    $age\n"; | 
| 474 | 0 | 0 |  |  |  | 0 | printf "%s - is less than the minimum age limit (%3.1d days old - limit is set to greater than %3d days)\n",$msg,(time - $age)/86400,-$agelimit/86400 if ($verbose > 1); | 
| 475 | 0 |  |  |  |  | 0 | return FALSE; | 
| 476 |  |  |  |  |  |  | } | 
| 477 |  |  |  |  |  |  | } | 
| 478 |  |  |  |  |  |  | } | 
| 479 | 24 | 100 |  |  |  | 47 | if ( ! $commit ) { | 
| 480 | 10 | 50 |  |  |  | 191 | print "$msg\n"  if ($verbose >= 1); | 
| 481 | 10 |  |  |  |  | 77 | return TRUE; | 
| 482 |  |  |  |  |  |  | } | 
| 483 | 14 | 50 |  |  |  | 510 | unless (&$md(dirname($B))){ # Make sure the parent of the target file exists | 
| 484 | 0 |  |  |  |  | 0 | print "parent of target $B does not exist\n"; | 
| 485 | 0 |  |  |  |  | 0 | printf "( %s )\n",dirname($B); | 
| 486 | 0 |  |  |  |  | 0 | return FALSE; | 
| 487 |  |  |  |  |  |  | } | 
| 488 | 14 | 100 |  |  |  | 296 | if ( -f $A ) { | 
| 489 | 9 | 100 |  |  |  | 206 | if ( -f $B ) {  # create a backup copy of target file $B | 
| 490 | 2 |  |  |  |  | 5 | $Btmp = $B . '.X'; | 
| 491 | 2 |  |  |  |  | 52 | while ( -f $Btmp ) {  #  Find a temporary file name to copy target to (allows rollback after a copy failure) | 
| 492 | 0 |  |  |  |  | 0 | $Btmp .= 'X'; | 
| 493 | 0 |  |  |  |  | 0 | print " *************** $Btmp\n";  # kind of error - this temp filename is already in use... | 
| 494 |  |  |  |  |  |  | } | 
| 495 | 2 | 50 |  |  |  | 135 | unless ( rename ($B, $Btmp) ) { # rename old copy of $B to $Btmp - to restore if the copy fails | 
| 496 | 0 |  |  |  |  | 0 | carp "Unable to create temp copy of $B ($Btmp) \n";  # carp if this fails - but continue..... | 
| 497 | 0 |  |  |  |  | 0 | undef $Btmp; | 
| 498 |  |  |  |  |  |  | } | 
| 499 |  |  |  |  |  |  | } | 
| 500 | 9 | 100 |  |  |  | 172 | if ( -d $B ){ # if the target file is a directory.... then remove it | 
| 501 | 2 |  |  |  |  | 76 | print " *************** removing directory tree $B to allow file copy\n"; | 
| 502 | 2 |  |  |  |  | 9 | &deltree2($B); | 
| 503 |  |  |  |  |  |  | } | 
| 504 |  |  |  |  |  |  |  | 
| 505 | 9 | 50 |  |  |  | 63 | if ( copy ($A,$B) ) { | 
| 506 | 9 | 100 |  |  |  | 2803 | print "$msg\n"  if ($verbose >= 1); | 
| 507 |  |  |  |  |  |  | # ****** | 
| 508 |  |  |  |  |  |  | # this needs modifying for UNIX | 
| 509 | 9 | 50 |  |  |  | 285 | chmod(0666,$B) if !($mode & 0x02); | 
| 510 | 9 | 50 |  |  |  | 225 | utime($Amtime,$Amtime,$B) || carp "Failed to set modification time on $B\n"; | 
| 511 | 9 | 50 |  |  |  | 237 | chmod(0444,$B) if !($mode & 0x02); | 
| 512 |  |  |  |  |  |  | # ****** | 
| 513 | 9 | 100 |  |  |  | 21 | if ( $Btmp ) {  # remove the temporary file created | 
| 514 | 2 |  | 33 |  |  | 166 | unlink $Btmp || carp "Failed to delete temporary file $Btmp\n"; | 
| 515 |  |  |  |  |  |  | } | 
| 516 | 9 |  |  |  |  | 53 | return TRUE; | 
| 517 |  |  |  |  |  |  | }else{ | 
| 518 | 0 |  |  |  |  | 0 | carp "$msg - failed to copy $A\n"; | 
| 519 | 0 | 0 |  |  |  | 0 | if ( $Btmp ) { | 
| 520 | 0 | 0 |  |  |  | 0 | unless ( rename ($Btmp, $B) ) {  # restore the temporary file after a copy failure | 
| 521 | 0 |  |  |  |  | 0 | carp "Unable to restore $B from temp copy of $Btmp following failed file copy\n"; | 
| 522 | 0 |  |  |  |  | 0 | undef $Btmp; | 
| 523 |  |  |  |  |  |  | } | 
| 524 |  |  |  |  |  |  | } | 
| 525 |  |  |  |  |  |  | } | 
| 526 |  |  |  |  |  |  | }else{ # $A is a directory... | 
| 527 | 5 | 50 |  |  |  | 88 | if ( -f $B ){ | 
| 528 |  |  |  |  |  |  | # if the target is a file - and needs to be a directory.... then remove it | 
| 529 | 0 |  |  |  |  | 0 | print " *************** removing file $B to allow replacement as a directory\n"; | 
| 530 | 0 |  | 0 |  |  | 0 | unlink $B || carp "Failed to delete file $B\n"; | 
| 531 |  |  |  |  |  |  | } | 
| 532 | 5 | 50 |  |  |  | 87 | if ( ! -d $B ) { | 
| 533 |  |  |  |  |  |  |  | 
| 534 | 0 | 0 |  |  |  | 0 | mkdir($B,0777) && return TRUE || carp "Unable to create directory $B\n"; | 
| 535 | 0 | 0 |  |  |  | 0 | print "$msg - (new directory)\n"  if ($verbose >= 1); | 
| 536 |  |  |  |  |  |  | } | 
| 537 |  |  |  |  |  |  | #  setting utime doe'nt work on a dir.  Maybe FS rules ?? | 
| 538 |  |  |  |  |  |  | } | 
| 539 | 5 |  |  |  |  | 20 | return FALSE; | 
| 540 | 9 |  |  |  |  | 57 | }; | 
| 541 |  |  |  |  |  |  |  | 
| 542 |  |  |  |  |  |  | #**************************************************************** | 
| 543 |  |  |  |  |  |  | # sub to test a directory tree exists, and if not to create it | 
| 544 |  |  |  |  |  |  | #**************************************************************** | 
| 545 |  |  |  |  |  |  | $md = sub { | 
| 546 | 18 |  |  | 18 |  | 27 | my($Dir) = @_; | 
| 547 | 18 | 50 |  |  |  | 40 | return TRUE unless $commit; | 
| 548 | 18 | 100 |  |  |  | 311 | if (! -d $Dir) { | 
| 549 | 5 |  |  |  |  | 16 | $Dir =~ /(.*)\/([^\/]*)/; | 
| 550 | 5 |  |  |  |  | 14 | my($parent,$dir) = ($1,$2); | 
| 551 | 5 | 100 |  |  |  | 75 | &$md($parent) if (!-d $parent);  # Create the parent if it does not exist | 
| 552 | 5 | 50 |  |  |  | 325 | mkdir ($Dir, 0777) || carp "Unable to create directory $Dir\n"; | 
| 553 |  |  |  |  |  |  | } | 
| 554 | 18 |  |  |  |  | 342 | return(-d $Dir); | 
| 555 | 9 |  |  |  |  | 30 | }; | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | #**************************************************************** | 
| 558 |  |  |  |  |  |  | # sub to delete directories / files | 
| 559 |  |  |  |  |  |  | #**************************************************************** | 
| 560 |  |  |  |  |  |  | $del = sub { | 
| 561 | 1 |  |  | 1 |  | 2 | my($targ, $mtime) = @_; | 
| 562 |  |  |  |  |  |  | #print "del ($targ,$mtime)\n" if $verbose > 3; | 
| 563 | 1 |  |  |  |  | 2 | my($msg); | 
| 564 | 1 | 50 |  |  |  | 46 | if (-d $targ) { | 
|  |  | 50 |  |  |  |  |  | 
| 565 | 0 |  |  |  |  | 0 | $msg = "  rmdir  $targ"; | 
| 566 |  |  |  |  |  |  | }elsif (-f $targ) { | 
| 567 | 1 |  |  |  |  | 3 | $msg = "  rm $targ"; | 
| 568 |  |  |  |  |  |  | } | 
| 569 | 1 | 50 | 33 |  |  | 6 | if ( $mtime && $agelimit ) { | 
| 570 | 0 | 0 |  |  |  | 0 | if ( $agelimit > 0) {  # if $agelimit is positive - ignore files older than the agelimit | 
| 571 | 0 | 0 |  |  |  | 0 | if ( ( $runtime - $mtime ) > $agelimit ) { | 
| 572 | 0 | 0 |  |  |  | 0 | printf "%s - exceeds age limit (%3.1d days - limit is newer than %3.1d days))\n", $msg, (time - $mtime)/(86400), $agelimit/(86400) if ( $verbose > 1 ); | 
| 573 | 0 |  |  |  |  | 0 | return FALSE; | 
| 574 |  |  |  |  |  |  | } | 
| 575 |  |  |  |  |  |  | }else{ #  if $agelimit is negative - ignore files newer than the agelimit | 
| 576 | 0 | 0 |  |  |  | 0 | if ( ( $runtime - $mtime ) < -$agelimit ) { | 
| 577 | 0 | 0 |  |  |  | 0 | printf "%s - under age limit (%3.1d days - limit is older than %3.1d days))\n", $msg, (time - $mtime)/(86400), -$agelimit/(86400) if ( $verbose > 1 ); | 
| 578 | 0 |  |  |  |  | 0 | return FALSE; | 
| 579 |  |  |  |  |  |  | } | 
| 580 |  |  |  |  |  |  | } | 
| 581 |  |  |  |  |  |  | }else{ | 
| 582 | 1 | 50 |  |  |  | 5 | print "$msg\n" if (($commit eq 0) & ($verbose >= 1)); | 
| 583 |  |  |  |  |  |  | } | 
| 584 | 1 | 50 |  |  |  | 3 | return TRUE unless $commit; | 
| 585 | 1 |  |  |  |  | 22 | print "$msg\n"; | 
| 586 | 1 | 50 |  |  |  | 42 | if (-d $targ) { | 
|  |  | 50 |  |  |  |  |  | 
| 587 | 0 | 0 |  |  |  | 0 | rmdir $targ || carp "Unable to delete directory $targ\n"; | 
| 588 | 0 |  |  |  |  | 0 | return ! -d $targ; | 
| 589 |  |  |  |  |  |  | }elsif (-f $targ) { | 
| 590 | 1 |  | 33 |  |  | 87 | unlink $targ || carp "Unable to delete file $targ\n"; | 
| 591 | 1 |  |  |  |  | 27 | return ! -f $targ; | 
| 592 |  |  |  |  |  |  | }else{ | 
| 593 | 0 |  |  |  |  | 0 | print "** DO SOMETHING HERE ** (NOT ORDINARY FILE OR DIRECTORY)\n"; | 
| 594 |  |  |  |  |  |  | } | 
| 595 | 0 |  |  |  |  | 0 | return FALSE; | 
| 596 | 9 |  |  |  |  | 35 | }; | 
| 597 |  |  |  |  |  |  |  | 
| 598 |  |  |  |  |  |  | #**************************************************************** | 
| 599 |  |  |  |  |  |  | # sub to delete directory trees | 
| 600 |  |  |  |  |  |  | # $targ    directory name | 
| 601 |  |  |  |  |  |  | # $dir     path to directory | 
| 602 |  |  |  |  |  |  | # $reftime reference to time hash (to allow record deletion) | 
| 603 |  |  |  |  |  |  | # $reftype reference to type hash (to allow record deletion) | 
| 604 |  |  |  |  |  |  | # $top      set true to remove top level directory | 
| 605 |  |  |  |  |  |  | #**************************************************************** | 
| 606 |  |  |  |  |  |  | $deltree = sub { | 
| 607 | 0 |  |  | 0 |  | 0 | my($targ,$dir,$reftime,$reftype,$top) = @_; | 
| 608 |  |  |  |  |  |  |  | 
| 609 |  |  |  |  |  |  | my $xxx = sub { | 
| 610 | 0 |  |  |  |  | 0 | ($tmp = $File::Find::name) =~ s/^\Q$dir//; # identify relative filename (to $dir) | 
| 611 | 0 | 0 |  |  |  | 0 | return if ($tmp eq $targ);                 # Don't remove top level directory unless $top == TRUE | 
| 612 | 0 | 0 |  |  |  | 0 | if (&$del($File::Find::name)) { | 
| 613 | 0 |  |  |  |  | 0 | delete $reftime->{$tmp}; | 
| 614 | 0 |  |  |  |  | 0 | delete $reftype->{$tmp}; | 
| 615 |  |  |  |  |  |  | } | 
| 616 | 0 |  |  |  |  | 0 | }; | 
| 617 | 0 |  |  |  |  | 0 | finddepth(\&$xxx, $dir . $targ); | 
| 618 | 0 | 0 |  |  |  | 0 | if ($top) { | 
| 619 | 0 |  |  |  |  | 0 | chdir $dir; | 
| 620 | 0 | 0 |  |  |  | 0 | if (rmdir "$dir$targ") { | 
| 621 | 0 |  |  |  |  | 0 | delete $reftime->{$targ}; | 
| 622 | 0 |  |  |  |  | 0 | delete $reftype->{$targ}; | 
| 623 |  |  |  |  |  |  | }else{ | 
| 624 | 0 |  |  |  |  | 0 | print "failed to rmdir $dir$targ\n"; | 
| 625 |  |  |  |  |  |  | } | 
| 626 |  |  |  |  |  |  | }else{ | 
| 627 | 0 | 0 |  |  |  | 0 | $commit ? $$reftime->{$targ} = (stat($dir . $targ))[9] : $$reftime->{$targ} = $runtime; | 
| 628 |  |  |  |  |  |  | } | 
| 629 | 9 |  |  |  |  | 32 | }; | 
| 630 |  |  |  |  |  |  |  | 
| 631 |  |  |  |  |  |  | #**************************************************************** | 
| 632 |  |  |  |  |  |  | # sub to truncate files to zero length | 
| 633 |  |  |  |  |  |  | #**************************************************************** | 
| 634 |  |  |  |  |  |  | $truncate = sub { | 
| 635 | 0 |  |  | 0 |  | 0 | my($file_ref, $mtime_ref) = @_; | 
| 636 | 0 | 0 |  |  |  | 0 | print " truncate $$file_ref\n" if ($verbose >= 2); | 
| 637 | 0 | 0 |  |  |  | 0 | if ($commit) { | 
| 638 | 0 | 0 |  |  |  | 0 | chmod(0666,$$file_ref)  || carp "Failed to chmod 0666 $$file_ref\n"; | 
| 639 | 0 | 0 |  |  |  | 0 | truncate($$file_ref, 0) || carp "Failed to truncate $$file_ref\n"; | 
| 640 |  |  |  |  |  |  | } | 
| 641 | 0 | 0 |  |  |  | 0 | $$mtime_ref = $commit ? (stat($$file_ref))[9] : $runtime; | 
| 642 | 0 |  |  |  |  | 0 | $$file_ref = undef; | 
| 643 | 0 |  |  |  |  | 0 | return TRUE; | 
| 644 | 9 |  |  |  |  | 33 | }; | 
| 645 |  |  |  |  |  |  |  | 
| 646 |  |  |  |  |  |  | #**************************************************************** | 
| 647 |  |  |  |  |  |  | # sub to touch files | 
| 648 |  |  |  |  |  |  | #**************************************************************** | 
| 649 |  |  |  |  |  |  | $touch = sub { | 
| 650 | 0 |  |  | 0 |  | 0 | my($file, $mtime_ref, $type_ref) = @_; | 
| 651 | 0 | 0 |  |  |  | 0 | print "   touch $file\n" if ($verbose >= 2); | 
| 652 | 0 | 0 |  |  |  | 0 | if ($commit) { | 
| 653 | 0 | 0 |  |  |  | 0 | open(FILE, ">> $file") || carp "Failed to touch $file\n"; | 
| 654 | 0 |  |  |  |  | 0 | close(FILE); | 
| 655 | 0 |  |  |  |  | 0 | chmod (0666,$file); | 
| 656 |  |  |  |  |  |  | } | 
| 657 | 0 | 0 |  |  |  | 0 | ($$type_ref, $$mtime_ref) = $commit ? (stat($file))[2,9] : $runtime; | 
| 658 | 9 |  |  |  |  | 31 | }; | 
| 659 |  |  |  |  |  |  | #**************************************************************** | 
| 660 |  |  |  |  |  |  | # sub to rename a file or directory | 
| 661 |  |  |  |  |  |  | #**************************************************************** | 
| 662 |  |  |  |  |  |  | $mv = sub { | 
| 663 | 0 |  |  | 0 |  | 0 | my($old,$new) = @_; | 
| 664 | 0 |  |  |  |  | 0 | my($msg) = "mv $old $new"; | 
| 665 | 0 | 0 |  |  |  | 0 | unless ($commit) { | 
|  |  | 0 |  |  |  |  |  | 
| 666 | 0 | 0 |  |  |  | 0 | print "$msg\n" if ($verbose > 1); | 
| 667 | 0 |  |  |  |  | 0 | return TRUE; | 
| 668 |  |  |  |  |  |  | }elsif ( rename ($old,$new)) { | 
| 669 | 0 | 0 |  |  |  | 0 | print "$msg\n" if ($verbose > 1); | 
| 670 | 0 |  |  |  |  | 0 | return TRUE; | 
| 671 |  |  |  |  |  |  | }else{ | 
| 672 | 0 |  |  |  |  | 0 | print "$msg - Failed \n"; | 
| 673 | 0 |  |  |  |  | 0 | return FALSE; | 
| 674 |  |  |  |  |  |  | } | 
| 675 | 9 |  |  |  |  | 38 | }; | 
| 676 |  |  |  |  |  |  |  | 
| 677 | 9 | 50 |  |  |  | 25 | benchmark("init") if $r_con->{bmark}; | 
| 678 | 9 | 100 |  |  |  | 23 | if ( $caller eq "Update" ) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 679 |  |  |  |  |  |  | #**************************************************************** | 
| 680 |  |  |  |  |  |  | # Delete tombstoned files (NH261100) | 
| 681 |  |  |  |  |  |  | #**************************************************************** | 
| 682 | 8 |  |  |  |  | 15 | foreach $tName (@$tfiles) { | 
| 683 | 0 |  |  |  |  | 0 | ($name = $tName) =~ s/.remove$//i; | 
| 684 |  |  |  |  |  |  |  | 
| 685 |  |  |  |  |  |  | # Delete  trees and touch a file with same name | 
| 686 | 0 | 0 |  |  |  | 0 | if (-d $r_con->{dira} . $tName) { | 
| 687 | 0 |  |  |  |  | 0 | &$deltree($tName, $r_con->{dira}, $refa, $refatype, TRUE); | 
| 688 | 0 |  |  |  |  | 0 | &$touch($r_con->{dira} . $tName, \$refa->{$tName}, $refatype->{$tName}); | 
| 689 |  |  |  |  |  |  | } | 
| 690 | 0 | 0 |  |  |  | 0 | if (-d $r_con->{dirb} . $tName) { | 
| 691 | 0 |  |  |  |  | 0 | &$deltree($tName, $r_con->{dirb}, $refb, $refbtype, TRUE); | 
| 692 | 0 |  |  |  |  | 0 | &$touch($r_con->{dirb} . $tName, \$refb->{$tName}, $refbtype->{$tName}); | 
| 693 |  |  |  |  |  |  | } | 
| 694 |  |  |  |  |  |  |  | 
| 695 |  |  |  |  |  |  | # Delete  trees and files | 
| 696 | 0 | 0 |  |  |  | 0 | if ($r_con->{nocase}) { | 
| 697 | 0 |  |  |  |  | 0 | ($aName) = grep { /^$name$/i } (keys %$refa); | 
|  | 0 |  |  |  |  | 0 |  | 
| 698 | 0 |  |  |  |  | 0 | ($bName) = grep { /^$name$/i } (keys %$refb); | 
|  | 0 |  |  |  |  | 0 |  | 
| 699 |  |  |  |  |  |  | }else{ | 
| 700 | 0 | 0 |  |  |  | 0 | $aName = ($refa->{$name}) ? $name : undef; | 
| 701 | 0 | 0 |  |  |  | 0 | $bName = ($refb->{$name}) ? $name : undef; | 
| 702 |  |  |  |  |  |  | } | 
| 703 | 0 | 0 |  |  |  | 0 | if ($aName) { | 
| 704 | 0 | 0 |  |  |  | 0 | if (-d $r_con->{dira} . $aName) { | 
| 705 |  |  |  |  |  |  | # Delete dir trees including top level dir | 
| 706 | 0 |  |  |  |  | 0 | &$deltree($aName, $r_con->{dira}, $refa, $refatype, TRUE); | 
| 707 |  |  |  |  |  |  | }else{ | 
| 708 | 0 | 0 |  |  |  | 0 | delete $refa->{$aName}, delete $refatype->{$aName} if &$del($r_con->{dira} . $aName); | 
| 709 |  |  |  |  |  |  | } | 
| 710 |  |  |  |  |  |  | } | 
| 711 | 0 | 0 |  |  |  | 0 | if ($bName) { | 
| 712 | 0 | 0 |  |  |  | 0 | if (-d $r_con->{dirb} . $bName) { | 
| 713 |  |  |  |  |  |  | # Delete dir trees including top level dir | 
| 714 | 0 |  |  |  |  | 0 | &$deltree($bName, $r_con->{dirb}, $refb, $refbtype, TRUE); | 
| 715 |  |  |  |  |  |  | }else{ | 
| 716 | 0 | 0 |  |  |  | 0 | delete $refb->{$bName}, delete $refbtype->{$bName} if &$del($r_con->{dirb} . $bName); | 
| 717 |  |  |  |  |  |  | } | 
| 718 |  |  |  |  |  |  | } | 
| 719 |  |  |  |  |  |  | } | 
| 720 |  |  |  |  |  |  | #**************************************************************** | 
| 721 |  |  |  |  |  |  | # Remove tombstone indicator files if older than $ttl (NH261100) | 
| 722 |  |  |  |  |  |  | # Truncate (which will also touch) nonzero byte files (NH070401) | 
| 723 |  |  |  |  |  |  | #**************************************************************** | 
| 724 | 8 |  |  |  |  | 21 | foreach (@$tfiles) { | 
| 725 | 0 | 0 |  |  |  | 0 | $afile = $refa->{$_} ? $r_con->{dira} . $_ : undef; | 
| 726 | 0 | 0 |  |  |  | 0 | $bfile = $refb->{$_} ? $r_con->{dirb} . $_ : undef; | 
| 727 | 0 | 0 | 0 |  |  | 0 | &$truncate(\$afile, \$refa->{$_}) if ($afile && -s $afile); | 
| 728 | 0 | 0 | 0 |  |  | 0 | &$truncate(\$bfile, \$refb->{$_}) if ($bfile && -s $bfile); | 
| 729 | 0 | 0 | 0 |  |  | 0 | delete $refa->{$_}, delete $refatype->{$_} if ($afile && (($refa->{$_} + $ttl) < $runtime) && &$del($afile)); | 
|  |  |  | 0 |  |  |  |  | 
| 730 | 0 | 0 | 0 |  |  | 0 | delete $refb->{$_}, delete $refbtype->{$_} if ($bfile && (($refb->{$_} + $ttl) < $runtime) && &$del($bfile)); | 
|  |  |  | 0 |  |  |  |  | 
| 731 |  |  |  |  |  |  | } | 
| 732 |  |  |  |  |  |  | # Note: modify arrays etc even if $commit is not set.  This is required to determine behaviour of code | 
| 733 |  |  |  |  |  |  | # without changing or deleting files and directories. | 
| 734 | 8 | 100 |  |  |  | 33 | if ( $mode =~ /^(A>B!?)|(A<>B)$/ ) { | 
| 735 | 3 |  |  |  |  | 8 | foreach (@$aonly) { | 
| 736 | 20 | 50 |  |  |  | 49 | next unless exists $refa->{$_}; | 
| 737 | 20 |  |  |  |  | 40 | $afile  = $r_con->{dira} . $_; | 
| 738 | 20 |  |  |  |  | 35 | $amtime = $refa->{$_} - $tz_bias_a + $tz_bias_b; | 
| 739 | 20 |  |  |  |  | 26 | $atype  = $refatype->{$_}; | 
| 740 | 20 |  |  |  |  | 38 | $bfile  = $r_con->{dirb} . $_; | 
| 741 |  |  |  |  |  |  | #print " $afile --> $bfile\n" if ($verbose >= 1); | 
| 742 | 20 | 100 |  |  |  | 43 | $refb->{$_} = $amtime, $refbtype->{$_} = $refatype->{$_} if &$fc($afile,$bfile,$amtime,0,"-->",$atype); | 
| 743 |  |  |  |  |  |  | } | 
| 744 |  |  |  |  |  |  | } | 
| 745 | 8 | 50 |  |  |  | 29 | if ( $mode =~ /^(AB)$/ ) { | 
| 746 | 0 |  |  |  |  | 0 | foreach (@$bonly) { | 
| 747 | 0 | 0 |  |  |  | 0 | next unless exists $refb->{$_}; | 
| 748 | 0 |  |  |  |  | 0 | $afile  = $r_con->{dira} . $_; | 
| 749 | 0 |  |  |  |  | 0 | $bfile  = $r_con->{dirb} . $_; | 
| 750 | 0 |  |  |  |  | 0 | $bmtime = $refb->{$_} - $tz_bias_b + $tz_bias_a; | 
| 751 | 0 |  |  |  |  | 0 | $btype  = $refbtype->{$_}; | 
| 752 |  |  |  |  |  |  | #print " $afile <-- $bfile\n" if ($verbose >= 1); | 
| 753 | 0 | 0 |  |  |  | 0 | $refa->{$_} = $bmtime, $refatype->{$_} = $refbtype->{$_} if &$fc($afile,$bfile,0,$bmtime,"<--",$btype); | 
| 754 |  |  |  |  |  |  | } | 
| 755 |  |  |  |  |  |  | } | 
| 756 | 8 | 50 |  |  |  | 17 | if ( $mode =~ /^A | 
| 757 | 0 |  |  |  |  | 0 | foreach (@$aonly) { | 
| 758 | 0 | 0 |  |  |  | 0 | next unless exists $refa->{$_}; | 
| 759 | 0 |  |  |  |  | 0 | $afile  = $r_con->{dira} . $_; | 
| 760 | 0 |  |  |  |  | 0 | $amtime = $refa->{$_}; | 
| 761 | 0 | 0 |  |  |  | 0 | delete $refa->{$_}, delete $refatype->{$_} if &$del($afile, $amtime); | 
| 762 |  |  |  |  |  |  | } | 
| 763 |  |  |  |  |  |  | } | 
| 764 | 8 | 50 |  |  |  | 18 | if ( $mode =~ /^A>B!$/ ) { | 
| 765 | 0 |  |  |  |  | 0 | foreach (@$bonly) { | 
| 766 | 0 | 0 |  |  |  | 0 | next unless exists $refb->{$_}; | 
| 767 | 0 |  |  |  |  | 0 | $bfile  = $r_con->{dirb} . $_; | 
| 768 | 0 |  |  |  |  | 0 | $bmtime = $refb->{$_}; | 
| 769 | 0 | 0 |  |  |  | 0 | delete $refb->{$_}, delete $refbtype->{$_} if &$del($bfile, $bmtime); | 
| 770 |  |  |  |  |  |  | } | 
| 771 |  |  |  |  |  |  | } | 
| 772 |  |  |  |  |  |  |  | 
| 773 | 8 |  |  |  |  | 26 | foreach $aName (keys %$common) { | 
| 774 |  |  |  |  |  |  | #    print "aName $aName\n"; | 
| 775 |  |  |  |  |  |  | #    printf "Ref: %s\n",$refa->{$aName}; | 
| 776 | 50 | 50 |  |  |  | 105 | next unless exists $refa->{$aName}; | 
| 777 |  |  |  |  |  |  | # To allow for non case sensitive filesystems | 
| 778 |  |  |  |  |  |  | # %common key holds the 'a' name and | 
| 779 |  |  |  |  |  |  | # %common value holds the 'b' name | 
| 780 | 50 |  |  |  |  | 103 | $bName  = $$common{$aName}; | 
| 781 | 50 |  |  |  |  | 76 | $amtime = $refa->{$aName} - $tz_bias_a; | 
| 782 | 50 |  |  |  |  | 64 | $bmtime = $refb->{$bName} - $tz_bias_b; | 
| 783 | 50 |  |  |  |  | 59 | $atype  = $refatype->{$aName}; | 
| 784 | 50 |  |  |  |  | 58 | $btype  = $refbtype->{$bName}; | 
| 785 | 50 |  |  |  |  | 82 | $afile  = $r_con->{dira} . $aName; | 
| 786 | 50 |  |  |  |  | 98 | $bfile  = $r_con->{dirb} . $bName; | 
| 787 |  |  |  |  |  |  |  | 
| 788 |  |  |  |  |  |  | # if btype is a file, and atype a directory, and we write a>b then ignore time rules | 
| 789 |  |  |  |  |  |  | # - we will remove the directory | 
| 790 | 50 | 100 |  |  |  | 178 | if (($btype & 0x01) & !($atype & 0x01)&( $mode =~ /^(a>b)|(a<>b)|(A>B)|(A>B!)$/ )) { | 
| 791 |  |  |  |  |  |  | #        printf "AFILE %s %s [%s]\n", $afile,$atype,($atype & 0x01); | 
| 792 |  |  |  |  |  |  | #        printf "BFILE %s %s [%s]\n", $bfile,$btype,($btype & 0x01); | 
| 793 | 2 | 50 |  |  |  | 8 | $refb->{$bName} = $amtime if (&$fc($afile,$bfile,$amtime,0,"-->",$atype)); | 
| 794 |  |  |  |  |  |  | # also need logic here to remove entries for any files in $bfile directory structure | 
| 795 |  |  |  |  |  |  | # from $refb, $btype etc | 
| 796 |  |  |  |  |  |  | } | 
| 797 |  |  |  |  |  |  |  | 
| 798 |  |  |  |  |  |  | # if atype is a file, and btype a directory, and we write a | 
| 799 |  |  |  |  |  |  | # - we will remove the directory | 
| 800 | 50 | 50 |  |  |  | 160 | if (($atype & 0x01) & !($btype & 0x01)&( $mode =~ /^(ab)|(A | 
| 801 |  |  |  |  |  |  | #        printf "AFILE %s %s [%s]\n", $afile,$atype,($atype & 0x01); | 
| 802 |  |  |  |  |  |  | #        printf "BFILE %s %s [%s]\n", $bfile,$btype,($btype & 0x01); | 
| 803 | 0 | 0 |  |  |  | 0 | $refa->{$aName} = $bmtime if (&$fc($afile,$bfile,$amtime,$bmtime,"<--",$btype)); | 
| 804 |  |  |  |  |  |  | # also need logic here to remove entries for any files in $afile directory structure | 
| 805 |  |  |  |  |  |  | # from $refa, $atype etc | 
| 806 |  |  |  |  |  |  | } | 
| 807 |  |  |  |  |  |  |  | 
| 808 | 50 | 50 |  |  |  | 105 | if ((($atype & 0x01) != ($btype & 0x01))&( $mode =~ /^(A<>B)$/ )) { | 
| 809 |  |  |  |  |  |  | #        printf "AFILE %s %s [%s]\n", $afile,$atype,($atype & 0x01); | 
| 810 |  |  |  |  |  |  | #        printf "BFILE %s %s [%s]\n", $bfile,$btype,($btype & 0x01); | 
| 811 | 0 |  |  |  |  | 0 | printf "objects %s and %s are different types, mode does not allow an action to be taken\n",$afile, $bfile; | 
| 812 |  |  |  |  |  |  | } | 
| 813 |  |  |  |  |  |  |  | 
| 814 |  |  |  |  |  |  |  | 
| 815 |  |  |  |  |  |  |  | 
| 816 |  |  |  |  |  |  | # Skip directories as their time can't be set (NH251100) | 
| 817 | 50 | 100 |  |  |  | 1010 | next if -d $afile; | 
| 818 |  |  |  |  |  |  |  | 
| 819 | 25 | 100 |  |  |  | 80 | if ( $amtime > ($bmtime + $fudge) ) { | 
|  |  | 100 |  |  |  |  |  | 
| 820 | 2 |  |  |  |  | 3 | $amtime += $tz_bias_b; | 
| 821 | 2 | 50 |  |  |  | 8 | if ( $mode =~ /^(a>b)|(a<>b)|(A>B)|(A>B!)|(A<>B)$/ ) { | 
| 822 |  |  |  |  |  |  | #if ( -f $afile ) { | 
| 823 |  |  |  |  |  |  | #  print " $afile --> $bfile\n" if ($verbose >= 1); | 
| 824 |  |  |  |  |  |  | #  print "  ($amtime) --> ($bmtime)\n" if ($verbose >= 2); | 
| 825 |  |  |  |  |  |  | #} | 
| 826 | 2 | 50 |  |  |  | 6 | $refb->{$bName} = $amtime if (&$fc($afile,$bfile,$amtime,$bmtime,"-->",$atype)); | 
| 827 |  |  |  |  |  |  | } | 
| 828 |  |  |  |  |  |  | }elsif ( ($amtime + $fudge) < $bmtime ) { | 
| 829 | 2 |  |  |  |  | 2 | $bmtime += $tz_bias_a; | 
| 830 | 2 | 50 |  |  |  | 10 | if ( $mode =~ /^(ab)|(AB)$/ ) { | 
| 831 |  |  |  |  |  |  | #if ( -f $afile ) { | 
| 832 |  |  |  |  |  |  | #  print " $afile <-- $bfile\n" if ($verbose >= 1); | 
| 833 |  |  |  |  |  |  | #  print "  ($amtime) <-- ($bmtime)\n" if ($verbose >= 2); | 
| 834 |  |  |  |  |  |  | #} | 
| 835 | 0 | 0 |  |  |  | 0 | $refa->{$aName} = $bmtime if (&$fc($afile,$bfile,$amtime,$bmtime,"<--",$btype)); | 
| 836 |  |  |  |  |  |  | } | 
| 837 |  |  |  |  |  |  | } | 
| 838 |  |  |  |  |  |  | } | 
| 839 |  |  |  |  |  |  | }elsif( $caller eq "Delete" ) { | 
| 840 | 1 |  |  |  |  | 2 | foreach my $f (@$amatch) { | 
| 841 | 1 | 50 |  |  |  | 6 | next unless exists $refa->{$f}; | 
| 842 | 1 |  |  |  |  | 2 | $afile  = $r_con->{dira} . $f; | 
| 843 | 1 |  |  |  |  | 2 | $amtime = $refa->{$f} - $tz_bias_a + $tz_bias_b; | 
| 844 | 1 |  |  |  |  | 3 | $atype  = $refatype->{$f}; | 
| 845 | 1 | 50 |  |  |  | 3 | if (&$del($afile, $amtime)) { | 
| 846 | 1 | 50 |  |  |  | 4 | if ($commit) { | 
| 847 | 1 |  |  |  |  | 3 | delete $refa->{$f}; | 
| 848 | 1 |  |  |  |  | 4 | delete $refatype->{$f}; | 
| 849 |  |  |  |  |  |  | # remove reference to this file from the arrays @aonly etc. | 
| 850 |  |  |  |  |  |  | } | 
| 851 |  |  |  |  |  |  | } | 
| 852 |  |  |  |  |  |  | } | 
| 853 |  |  |  |  |  |  |  | 
| 854 |  |  |  |  |  |  | }elsif( $caller eq "Rename" ) { | 
| 855 | 0 |  |  |  |  | 0 | $nsub =~ m/^(.)/; | 
| 856 | 0 |  |  |  |  | 0 | my($sep) = ($1); | 
| 857 | 0 | 0 |  |  |  | 0 | if ( $nsub =~ m/^$sep(.*)$sep(.*)$sep(.*)?$/ ) { | 
| 858 | 0 |  |  |  |  | 0 | my($match,$replace,$arg) = ($1,$2,$3); | 
| 859 |  |  |  |  |  |  | #      print "nsub $nsub\n"; | 
| 860 |  |  |  |  |  |  | #      print "sep: $sep\nmatch : $match\n replace: $replace\narg: $arg\n"; | 
| 861 | 0 |  |  |  |  | 0 | foreach my $f (@$amatch) { | 
| 862 | 0 | 0 |  |  |  | 0 | next unless exists $refa->{$f}; | 
| 863 | 0 |  |  |  |  | 0 | my($newname) = $f; | 
| 864 | 0 |  |  |  |  | 0 | $newname =~ s/$match/$replace/; | 
| 865 | 0 | 0 |  |  |  | 0 | next if ($newname eq $f); # next file if no change... | 
| 866 | 0 |  |  |  |  | 0 | $afile  = $r_con->{dira} . $f; | 
| 867 | 0 |  |  |  |  | 0 | my ($Afile) = $r_con->{dira} . $newname; | 
| 868 | 0 | 0 |  |  |  | 0 | if (&$mv($afile,$Afile)){ | 
| 869 | 0 |  |  |  |  | 0 | $refa->{$Afile}     = $refa->{$f}; | 
| 870 | 0 |  |  |  |  | 0 | $refatype->{$Afile} = $refatype->{$f}; | 
| 871 | 0 |  |  |  |  | 0 | delete $refa->{$f}; | 
| 872 | 0 |  |  |  |  | 0 | delete $refatype->{$f}; | 
| 873 |  |  |  |  |  |  | } | 
| 874 |  |  |  |  |  |  | } | 
| 875 |  |  |  |  |  |  | }else{ | 
| 876 | 0 |  |  |  |  | 0 | carp "unable to understand substition  argument $nsub\n"; | 
| 877 |  |  |  |  |  |  | } | 
| 878 |  |  |  |  |  |  | } | 
| 879 |  |  |  |  |  |  |  | 
| 880 |  |  |  |  |  |  | #  add references to allow @aonly, @bonly etc to be recalled from the reference | 
| 881 | 9 |  |  |  |  | 15 | my($retval); | 
| 882 | 9 |  |  |  |  | 21 | $retval->{amatch} = $amatch; | 
| 883 | 9 |  |  |  |  | 17 | $retval->{bmatch} = $bmatch; | 
| 884 | 9 |  |  |  |  | 13 | $retval->{aonly}  = $aonly; | 
| 885 | 9 |  |  |  |  | 15 | $retval->{bonly}  = $bonly; | 
| 886 | 9 |  |  |  |  | 11 | $retval->{common} = $common; | 
| 887 | 9 | 50 |  |  |  | 22 | benchmark("synch files") if $r_con->{bmark}; | 
| 888 | 9 |  |  |  |  | 486 | return $retval; | 
| 889 |  |  |  |  |  |  | } | 
| 890 |  |  |  |  |  |  |  | 
| 891 |  |  |  |  |  |  | #===================================================================== | 
| 892 |  |  |  |  |  |  | # Support old method call | 
| 893 |  |  |  |  |  |  | sub SetDefaults { | 
| 894 | 0 |  |  | 0 | 0 | 0 | return New @_; | 
| 895 |  |  |  |  |  |  | } | 
| 896 |  |  |  |  |  |  | #===================================================================== | 
| 897 |  |  |  |  |  |  | sub _arraysort { | 
| 898 | 9 |  |  | 9 |  | 16 | my($r_con, $regex, $negregex, $nocase) = @_; | 
| 899 | 9 |  |  |  |  | 12 | my(@amatch,@bmatch,@tfiles,%common,@aonly,@bonly,@temp,$name,$mtime,$type); | 
| 900 | 0 |  |  |  |  | 0 | my(@sorted_amatch,@sorted_bmatch,$aName,$bName,$aIndex,$bIndex); | 
| 901 | 9 |  |  |  |  | 17 | my %dup      = (); | 
| 902 | 9 |  |  |  |  | 20 | my $refa     = $r_con->{alist}; | 
| 903 | 9 |  |  |  |  | 12 | my $refatype = $r_con->{atype}; | 
| 904 | 9 |  |  |  |  | 15 | my $refb     = $r_con->{blist}; | 
| 905 | 9 |  |  |  |  | 11 | my $refbtype = $r_con->{btype}; | 
| 906 | 9 | 50 |  |  |  | 20 | my $regexextn = $nocase ? '(?i)' : '';  # use regex extention (?i) if working with case insensitive file systems | 
| 907 |  |  |  |  |  |  |  | 
| 908 |  |  |  |  |  |  | # Find files matching the regex in dira | 
| 909 | 9 | 100 |  |  |  | 39 | print "Files Matching regex in $r_con->{dira}:\n" if ($r_con->{verbose} >= 4); | 
| 910 | 9 |  |  |  |  | 46 | foreach $name (keys %$refa) { | 
| 911 | 90 | 100 | 66 |  |  | 798 | if ( $name && ($name =~ /$regexextn$regex/) && ($name !~ /$regexextn$negregex/) ) { | 
|  |  |  | 66 |  |  |  |  | 
| 912 | 81 |  |  |  |  | 164 | push (@amatch,$name); | 
| 913 |  |  |  |  |  |  | #printf"  %s  %s  %s\n",%$refa->{$name},%$refatype->{$name},$name if ($r_con->{verbose} >= 4); | 
| 914 |  |  |  |  |  |  | } | 
| 915 |  |  |  |  |  |  | } | 
| 916 |  |  |  |  |  |  | # Find files matching the regex in dirb | 
| 917 | 9 | 100 |  |  |  | 50 | print "Files Matching regex in $r_con->{dirb}:\n" if ($r_con->{verbose} >= 4); | 
| 918 | 9 |  |  |  |  | 30 | foreach $name (keys %$refb) { | 
| 919 | 60 | 100 | 66 |  |  | 500 | if ( $name && $name =~ /$regexextn$regex/ && $name !~ /$regexextn$negregex/ ) { | 
|  |  |  | 66 |  |  |  |  | 
| 920 | 51 |  |  |  |  | 100 | push (@bmatch,$name); | 
| 921 |  |  |  |  |  |  | #printf"  %s  %s  %s\n",%$refb->{$name},%$refbtype->{$name},$name if ($r_con->{verbose} >= 4); | 
| 922 |  |  |  |  |  |  | } | 
| 923 |  |  |  |  |  |  | } | 
| 924 |  |  |  |  |  |  | # Build a list of files that have an added extension ".remove" - to be deleted by tombstone routines later | 
| 925 | 9 |  |  |  |  | 20 | @tfiles =     grep { /.+\.remove$/i } @amatch;  # get alist files/dirs with .remove extension | 
|  | 81 |  |  |  |  | 332 |  | 
| 926 | 9 |  |  |  |  | 13 | push @tfiles, grep { /.+\.remove$/i } @bmatch;  # get blist files/dirs with .remove extension | 
|  | 51 |  |  |  |  | 185 |  | 
| 927 | 9 |  |  |  |  | 17 | @tfiles =     grep { ! $dup{$_} ++  } @tfiles;  # remove duplicates | 
|  | 0 |  |  |  |  | 0 |  | 
| 928 |  |  |  |  |  |  |  | 
| 929 |  |  |  |  |  |  | # Find elements that are common/unique to @amatch and @bmatch | 
| 930 |  |  |  |  |  |  | # -put in sorted order so that we can create directories/files | 
| 931 |  |  |  |  |  |  | #  in one sweep (ie we don't try to create a file before its | 
| 932 |  |  |  |  |  |  | #  parent directory exists) | 
| 933 |  |  |  |  |  |  | # | 
| 934 |  |  |  |  |  |  | # On non-case sensitive filesystems (e.g Bill's) ignore the case | 
| 935 |  |  |  |  |  |  | # when testing for matching files.  This will still allow repl to | 
| 936 |  |  |  |  |  |  | # create / update files maintaining their original case. (NH311000) | 
| 937 | 9 |  |  |  |  | 11 | $aIndex = 0; | 
| 938 | 9 |  |  |  |  | 12 | $bIndex = 0; | 
| 939 | 9 | 50 |  |  |  | 38 | @sorted_amatch = $nocase ? sort {lc($a) cmp lc($b) } @amatch : sort @amatch; | 
|  | 172 |  |  |  |  | 255 |  | 
| 940 | 9 | 50 |  |  |  | 24 | @sorted_bmatch = $nocase ? sort {lc($a) cmp lc($b) } @bmatch : sort @bmatch; | 
|  | 106 |  |  |  |  | 141 |  | 
| 941 |  |  |  |  |  |  |  | 
| 942 | 9 |  |  |  |  | 23 | while ( $aIndex < @sorted_amatch ) { | 
| 943 | 54 | 100 |  |  |  | 112 | last unless defined $sorted_bmatch[$bIndex];  # End of b list | 
| 944 | 51 |  |  |  |  | 64 | $aName = $sorted_amatch[$aIndex]; | 
| 945 | 51 |  |  |  |  | 58 | $bName = $sorted_bmatch[$bIndex]; | 
| 946 | 51 | 50 | 33 |  |  | 135 | if ($aName eq $bName || ($nocase && lc($aName) eq lc($bName))) { | 
|  |  | 0 | 66 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 947 | 51 |  |  |  |  | 75 | $common{$aName} = $bName;  # Store $aName as key and $bName as value | 
| 948 | 51 |  |  |  |  | 52 | $aIndex++; | 
| 949 | 51 |  |  |  |  | 110 | $bIndex++; | 
| 950 |  |  |  |  |  |  | }elsif (($nocase && lc($aName) lt lc($bName)) || (!$nocase && $aName lt $bName)) { | 
| 951 | 0 |  |  |  |  | 0 | push(@aonly,$aName); | 
| 952 | 0 |  |  |  |  | 0 | $aIndex++; | 
| 953 |  |  |  |  |  |  | }else{ | 
| 954 | 0 |  |  |  |  | 0 | push(@bonly,$bName); | 
| 955 | 0 |  |  |  |  | 0 | $bIndex++; | 
| 956 |  |  |  |  |  |  | } | 
| 957 |  |  |  |  |  |  | } | 
| 958 |  |  |  |  |  |  | # Get any remainder of 'a' list | 
| 959 | 9 |  |  |  |  | 22 | while ( $aIndex < @sorted_amatch ) { | 
| 960 | 30 |  |  |  |  | 70 | push(@aonly,$sorted_amatch[$aIndex++]); | 
| 961 |  |  |  |  |  |  | } | 
| 962 |  |  |  |  |  |  | # Get any remainder of 'b' list | 
| 963 | 9 |  |  |  |  | 23 | while ($bIndex < @sorted_bmatch) { | 
| 964 | 0 |  |  |  |  | 0 | push(@bonly,$sorted_bmatch[$bIndex++]); | 
| 965 |  |  |  |  |  |  | } | 
| 966 |  |  |  |  |  |  |  | 
| 967 | 9 |  |  |  |  | 30 | @aonly = reverse sort @aonly; # Sort so that file preceed directories - allows deltree to delete | 
| 968 | 9 |  |  |  |  | 15 | @bonly = reverse sort @bonly; # files before directories | 
| 969 |  |  |  |  |  |  |  | 
| 970 | 9 | 100 |  |  |  | 21 | if ( $r_con->{verbose} >= 3 ) { | 
| 971 | 1 |  |  |  |  | 19 | print "Common Files :\n"; | 
| 972 | 1 |  |  |  |  | 5 | foreach (keys %common) { | 
| 973 | 0 |  |  |  |  | 0 | print "  $_\n  $common{$_}\n"; | 
| 974 |  |  |  |  |  |  | } | 
| 975 | 1 |  |  |  |  | 16 | print "A dir only Files :\n"; | 
| 976 | 1 |  |  |  |  | 4 | foreach (@aonly) { | 
| 977 | 10 |  |  |  |  | 162 | print "  $_\n"; | 
| 978 |  |  |  |  |  |  | } | 
| 979 | 1 |  |  |  |  | 17 | print "B dir only Files :\n"; | 
| 980 | 1 |  |  |  |  | 4 | foreach (@bonly) { | 
| 981 | 0 |  |  |  |  | 0 | print "  $_\n"; | 
| 982 |  |  |  |  |  |  | } | 
| 983 | 1 |  |  |  |  | 46 | print "\n\nEnd of File Lists ...\n\n"; | 
| 984 |  |  |  |  |  |  | } | 
| 985 | 9 |  |  |  |  | 80 | return (\@tfiles, \%common, \@aonly, \@bonly, \@amatch, \@bmatch); | 
| 986 |  |  |  |  |  |  | } | 
| 987 |  |  |  |  |  |  | #===================================================================== | 
| 988 |  |  |  |  |  |  | #  If this is called with with "init" argument this initialises global variable @times | 
| 989 |  |  |  |  |  |  | #  - a record of user and system times; | 
| 990 |  |  |  |  |  |  | #  Otherwise difference since last init (user and system times) is printed to STDOUT | 
| 991 |  |  |  |  |  |  | my @times; # global var | 
| 992 |  |  |  |  |  |  | sub benchmark ($@) { | 
| 993 | 0 |  |  | 0 | 0 | 0 | my($str,$r1,$u1,$s1) = @_; | 
| 994 | 0 | 0 |  |  |  | 0 | @times = $r1 ? ($r1,$u1,$s1) : ( $runtime, times), return if $str eq "init"; | 
|  |  | 0 |  |  |  |  |  | 
| 995 | 0 | 0 |  |  |  | 0 | ($r1,$u1,$s1)   = @times unless $r1; | 
| 996 | 0 |  |  |  |  | 0 | my($r2,$u2,$s2) = ( $runtime, times); | 
| 997 | 0 |  |  |  |  | 0 | printf "  %-13s: %2d secs  ( %.2f usr + %.2f sys = %.2f CPU )\n", | 
| 998 |  |  |  |  |  |  | $str, $r2-$r1, $u2-$u1, $s2-$s1, $u2-$u1 + $s2-$s1; | 
| 999 |  |  |  |  |  |  | } | 
| 1000 |  |  |  |  |  |  | #===================================================================== | 
| 1001 |  |  |  |  |  |  | sub elapsed{ | 
| 1002 | 19 |  |  | 19 | 0 | 28 | my($start_time)=@_; | 
| 1003 | 19 |  |  |  |  | 33 | my($elapsed) = $start_time - time; | 
| 1004 | 19 |  |  |  |  | 468 | return $elapsed; | 
| 1005 |  |  |  |  |  |  | } | 
| 1006 |  |  |  |  |  |  | #===================================================================== | 
| 1007 |  |  |  |  |  |  | sub deltree2{ | 
| 1008 | 2 |  |  | 2 | 0 | 5 | my($root)=@_; | 
| 1009 | 2 |  |  |  |  | 2 | my(@dirlist); | 
| 1010 |  |  |  |  |  |  | sub finddir { | 
| 1011 | 2 | 50 |  | 2 | 0 | 19 | if ( -d ) { | 
| 1012 | 2 |  |  |  |  | 96 | push (@dirlist,$File::Find::name); | 
| 1013 |  |  |  |  |  |  | } | 
| 1014 |  |  |  |  |  |  | } | 
| 1015 |  |  |  |  |  |  | sub findfile { | 
| 1016 | 2 | 50 |  | 2 | 0 | 139 | if ( -f ) { | 
| 1017 | 0 | 0 |  |  |  |  | print " -   unable to delete $File::Find::name \n" if (! unlink $File::Find::name ); | 
| 1018 |  |  |  |  |  |  | } | 
| 1019 |  |  |  |  |  |  | } | 
| 1020 | 2 |  |  |  |  | 133 | find(\&findfile,$root); | 
| 1021 | 2 |  |  |  |  | 122 | find(\&finddir,$root); | 
| 1022 | 2 |  |  |  |  | 8 | while ( @dirlist ) { | 
| 1023 | 1 |  |  |  |  | 2 | my $dir = pop(@dirlist); | 
| 1024 | 1 | 50 |  |  |  | 110 | print "   -   unable to remove $dir\n" if (! rmdir($dir) ); | 
| 1025 |  |  |  |  |  |  | } | 
| 1026 | 2 | 100 |  |  |  | 168 | print "   -   unable to remove $root\n" if (! rmdir($root) ); | 
| 1027 |  |  |  |  |  |  |  | 
| 1028 |  |  |  |  |  |  | } | 
| 1029 |  |  |  |  |  |  | #======================================================================== | 
| 1030 |  |  |  |  |  |  | 1; | 
| 1031 |  |  |  |  |  |  | __END__ |