File Coverage

blib/lib/File/Repl.pm
Criterion Covered Total %
statement 281 491 57.2
branch 144 392 36.7
condition 14 72 19.4
subroutine 24 33 72.7
pod 7 13 53.8
total 470 1001 46.9


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__