root/gs2-extensions/parallel-building/trunk/src/perllib/FileUtils.pm @ 27421

Revision 27421, 21.9 KB (checked in by jmt12, 7 years ago)

Fixing bug in filePutContents (wasn't shifting parameters off front of @_, but then was readding them), adding canRead(), and changing some comment formatting

Line 
1###########################################################################
2#
3# FileUtils.pm -- functions for dealing with files. Will delegate to the
4#                 appropriate filesystem driver based upon any file
5#                 protocol specified and dependent on configuration as
6#                 defined by the collection admin
7#
8# A component of the Greenstone digital library software
9# from the New Zealand Digital Library Project at the
10# University of Waikato, New Zealand.
11#
12# Copyright (C) 2013 New Zealand Digital Library Project
13#
14# This program is free software; you can redistribute it and/or modify
15# it under the terms of the GNU General Public License as published by
16# the Free Software Foundation; either version 2 of the License, or
17# (at your option) any later version.
18#
19# This program is distributed in the hope that it will be useful,
20# but WITHOUT ANY WARRANTY; without even the implied warranty of
21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22# GNU General Public License for more details.
23#
24# You should have received a copy of the GNU General Public License
25# along with this program; if not, write to the Free Software
26# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27#
28###########################################################################
29
30package FileUtils;
31
32# Perl Modules
33use strict;
34use Symbol qw<qualify>;
35
36# Greenstone Modules
37use util;
38
39# Configuration
40my $debug = 0;
41
42## @function _callFunction($driver_name, $function_name, ...)
43#
44# Make a function call to a dynamically loaded database driver.
45# @param $driver_name - The name of the file protocol driver to load
46# @param $function_name - The function within the driver to call
47# @param <rest> - The parameters to be passed to the function called
48#
49sub _callFunction
50{
51  my $driver_name = shift(@_);
52  my $function_name = shift(@_);
53  &_prettyPrint(0, $driver_name, $function_name, @_) unless (!$debug);
54  # Need to look within fileutils directory
55  my $package_name = 'FileUtils::' . $driver_name;
56  # Try to load the requested infodb type
57  if (!&_loadDriver($package_name))
58  {
59    &printError('Failed to load requested file protocol driver: ' . $package_name, 1);
60  }
61  # Then make call to the newly created package
62  no strict;
63  # Better check that the function we are about to call exists
64  my $symbol = qualify($function_name, $package_name);
65  unless ( defined &{$symbol} )
66  {
67    &printError('Function not found: ' . $package_name . '::' . $function_name, 1);
68  }
69  # Call the function and get result if applicable
70  my $result = &{$symbol}(@_);
71  &_prettyPrint(1, $result) unless (!$debug);
72  return $result;
73}
74## callFunction()
75
76## @function _prettyPrint()
77#
78# Print a debugging message to STDERR constructed from the <rest> based upon
79# the type.
80# @param type - If 0, output the start of a function with a listing of its
81#               parameters. If 1, output the result of a function. 2 is
82#               used for function errors. Default to simply printing what-
83#               ever else is in <rest>
84#
85sub _prettyPrint
86{
87  my $type = shift(@_);
88  if (!defined $type || $type > 2)
89  {
90    $type = -1;
91  }
92  my ($package, $filename, $line, $function) = caller(1);
93  my $message;
94  # Start of a function
95  if (0 == $type)
96  {
97    $message = $package . '::' . $function . '(';
98    my $argument = shift(@_);
99    my $first = 1;
100    while (defined $argument)
101    {
102      if (!$first)
103      {
104        $message .= ', ';
105      }
106      else
107      {
108        $first = 0;
109      }
110      if ($argument =~ /\D/)
111      {
112        $message .= '"' . $argument . '"';
113      }
114      else
115      {
116        $message .= $argument;
117      }
118      $argument = shift(@_);
119    }
120    $message .= ')';
121  }
122  # Result of a function
123  elsif (1 == $type)
124  {
125    $message = $package . '::' . $function . '() => ';
126    my $result = shift(@_);
127    if ($result =~ /\D/)
128    {
129      $message .= '"' . $result . '"';
130    }
131    else
132    {
133      $message .= $result;
134    }
135  }
136  elsif (2 == $type)
137  {
138    my $error = shift(@_);
139    $message = 'Error in ' . $package . '::' . $function . '()! ' . $error;
140  }
141  # Else we leave the message as it is
142  else
143  {
144    $message = join("\n", @_);
145  }
146  print STDERR "[" . time() . "] " . $message . "\n";
147}
148## _prettyPrint()
149
150# /** @function _determineDriver()
151#  *  Given a file path determine the appropriate protocol. For now anything
152#  *  other than a full path beginning with an explicit protocol will default
153#  *  to using 'local' file functions.
154#  *  @return 'local'
155#  */
156sub _determineDriver
157{
158  my $path = shift(@_);
159  &_prettyPrint(0, $path) unless (!$debug);
160  # Determine the appropriate driver from the protocol
161  my $driver = 'LocalFS';
162  # - this is were I'll eventually have the ability to configure
163  #   what driver handles what protocol, hopefully from the collect.cfg
164  my $colon_index = index($path, ':');
165  if ($colon_index > -1)
166  {
167    my $protocol = substr($path, 0, $colon_index);
168    # check the perl module exists
169    eval
170    {
171      require 'FileUtils/' . $protocol . '.pm';
172    };
173    if ($@)
174    {
175      die($@);
176      print STDERR 'Warning! FileUtils::_determineDriver() driver not found (defaulting to local filesystem):' . $protocol . "\n" . $@ . "\n";
177    }
178    else
179    {
180      $driver = $protocol;
181    }
182  }
183  &_prettyPrint(1, $driver) unless (!$debug);
184  return $driver;
185}
186# /** _determineDriver()
187
188# /** @function _loadDriver($class, ...)
189#  *  Runtime class loading for use in FileUtils to load various protocol
190#  *  drivers, possibly configured in the collect.cfg, at runtime.
191#  *  @param $class - The class name (including any path) to load
192#  *  @param <rest> - any function aliases you want exported
193#  */
194sub _loadDriver
195{
196  my $class = shift(@_);
197  &_prettyPrint(0, $class) unless (!$debug);
198  # Convert the Perl Module-like name into a file path
199  (my $file = "$class.pm") =~ s|::|/|g;
200  # - ensure we haven't already loaded this class
201  unless( $INC{$file} )
202  {
203    require $file;
204  }
205  # - this is the magic that actually instantiates the class (rubberstamp?)
206  # - we pass @_ to action any function aliases exports requested
207  eval
208  {
209    $class->import(@_);
210  };
211  # - by now the driver file should have been loaded
212  my $result = defined $INC{$file};
213  &_prettyPrint(1, $result) unless (!$debug);
214  return $result;
215}
216# /** _loadDriver($class, ...) **/
217
218################################################################################
219
220
221## @function printError()
222#
223sub printError
224{
225  my ($message, $fatal) = @_;
226  my ($package, $filename, $line, $function) = caller(1);
227  if (defined $!)
228  {
229    $message .= ' (' . $! . ')';
230  }
231  if (defined $fatal && $fatal)
232  {
233    die('Fatal Error! ' . $package . '::' . $function . '() - ' . $message ."\n");
234  }
235  else
236  {
237    print STDERR 'Error! ' . $package . '::' . $function . '() - ' . $message ."\n";
238  }
239}
240## printError()
241
242
243## @function printWarning
244#
245sub printWarning
246{
247  my ($message) = @_;
248  my ($package, $filename, $line, $function) = caller(1);
249  print STDERR 'Warning! ' . $package . '::' . $function . '() - ' . $message . "\n";
250}
251## printWarning()
252
253################################################################################
254######################## Legacy function name mappings  ########################
255################################################################################
256# Note: there are lots of functions involving files/directories/paths etc found
257# in utils.pm that are not represented here. My intention was to just have those
258# functions that need to be dynamic based on filesystem, or need some rejigging
259# to be filesystem aware. This is an argument, I guess, for moving some of the
260# other functions here so that they are nicely encapsulated - but the question
261# is what to do with functions like filename_within_directory_url_format() which
262# is more URL based than file based... dunno.
263################################################################################
264
265sub cachedir {return synchronizeDirectory(@_);}
266sub cp {return copyFiles(@_);}
267sub cp_r {print "implement cp_r()";}
268sub cp_r_nosvn {print "implement cp_r_nosvn()";}
269sub cp_r_toplevel {print "implement cp_r_toplevel()";}
270sub differentfiles {return &differentFiles(@_);}
271sub dir_exists {return &directoryExists(@_);}
272sub file_exists {return &fileExists(@_);}
273sub file_lastmodified {return &modificationTime(@_);}
274sub file_readdir {return readDirectory(@_);}
275sub file_size {return &fileSize(@_);}
276sub filename_cat {return filenameConcatenate(@_);}
277sub filename_is_absolute {return &isFilenameAbsolute();};
278sub filtered_rm_r {print "implement filtered_rm_r()";}
279sub hard_link {print "implement hard_link()";}
280sub is_dir_empty {return &isDirectoryEmpty();}
281sub mk_all_dir {return &makeAllDirectories(@_);}
282sub mk_dir {return &makeDirectory(@_);}
283sub mv {return &moveFiles(@_);}
284sub mv_dir_contents {print "implement mv_dir_contents()";}
285sub rm {print "implement rm()";}
286sub rm_debug {print "implement rm_debug()";}
287sub rm_r {print "implement rm_r()";}
288sub soft_link {print "implement soft_link()";}
289
290################################################################################
291########## Common functions  ##########
292################################################################################
293# Note: these are the file-based functions that are not dynamic in themselves,
294# but that need significant changes to support multiple possible filesystems.
295################################################################################
296
297
298## @function differentFiles
299# (previous util.pm version used -e, -d, and 'stat', none of which support
300#  *  filesystems such as hadoop)
301#  */
302sub differentFiles
303{
304  my ($file1, $file2, $verbosity) = @_;
305  if (!defined $verbosity)
306  {
307    $verbosity = 1
308  }
309
310  # remove trailing slashes
311  $file1 =~ s/\/+$//;
312  $file2 =~ s/\/+$//;
313
314  # chop off the last part of the path as the file/dir name
315  my ($file1name) = $file1 =~ /\/([^\/]*)$/;
316  my ($file2name) = $file2 =~ /\/([^\/]*)$/;
317
318  # - cheapest first; test the two filename strings are the same
319  if ($file1name ne $file2name)
320  {
321    print STDERR "filenames are not the same\n" if ($verbosity >= 2);
322    return 1;
323  }
324
325  if (!&pathExists($file1) || !&pathExists($file2))
326  {
327    print STDERR "one or other file doesn't exist\n" if ($verbosity >= 2);
328    return -1;
329  }
330
331  if (&directoryExists($file1))
332  {
333    if (!&directoryExists($file2))
334    {
335      print STDERR "one file is a directory\n" if ($verbosity >= 2);
336      return 1;
337    }
338    return 0;
339  }
340
341  # both must be regular files
342  unless (&fileExists($file1) && &fileExists($file2))
343  {
344    print STDERR "one file is not a regular file\n" if ($verbosity >= 2);
345    return 1;
346  }
347
348  # the size of the files must be the same
349  if (&fileSize($file1) != &fileSize($file2))
350  {
351    print STDERR "different sized files\n" if ($verbosity >= 2);
352    return 1;
353  }
354
355  # the second file cannot be older than the first
356  if (&modificationTime($file1) > &modificationTime($file2))
357  {
358    print STDERR "file is older\n" if ($verbosity >= 2);
359    return 1;
360  }
361
362  return 0;
363}
364# /** differentFiles() **/
365
366
367## @function fileGetContents()
368#
369sub fileGetContents
370{
371  my ($path) = @_;
372  my $content;
373  my $driver = &FileUtils::_determineDriver($path);
374  my $filesize = &FileUtils::_callFunction($driver, 'fileSize', $path);
375  my $fh;
376  &FileUtils::_callFunction($driver, 'openFileHandle', $path, '<', \$fh);
377  sysread($fh, $content, $filesize);
378  &FileUtils::_callFunction($driver, 'closeFileHandle', \$fh);
379  return $content;
380}
381## fileGetContents()
382
383
384## @function filePutContents()
385#
386sub filePutContents
387{
388  my $path = shift(@_);
389  my $str = shift(@_);
390  my $driver = &FileUtils::_determineDriver($path);
391  my $fh;
392  &FileUtils::_callFunction($driver, 'openFileHandle', $path, '>', \$fh);
393  print $fh $str;
394  &FileUtils::_callFunction($driver, 'closeFileHandle', \$fh);
395  return 1;
396}
397## filePutContents(path, str)
398
399
400## @function sanitizePath()
401#
402sub sanitizePath
403{
404  my ($path) = @_;
405  # fortunately filename concatenate will perform all the double slash removal,
406  # end slash removal we need, and in a protocol aware fashion
407  return &filenameConcatenate($path);
408}
409## sanitizePath()
410
411
412################################################################################
413
414
415## @function canRead()
416#
417sub canRead
418{
419  my $path = shift(@_);
420  my $driver = &FileUtils::_determineDriver($path);
421  return &FileUtils::_callFunction($driver, 'canRead', $path, @_);
422}
423## canRead()
424
425
426## @function closeFileHandle()
427#
428sub closeFileHandle
429{
430  my $path = shift(@_);
431  my $driver = &FileUtils::_determineDriver($path);
432  return &FileUtils::_callFunction($driver, 'closeFileHandle', @_);
433}
434## closeFileHandle()
435
436# /**
437#  */
438sub copyFiles
439{
440  return &transferFiles(@_, 'COPY');
441}
442# /** copyFiles() **/
443
444# /**
445#  */
446sub directoryExists
447{
448  my $path = shift(@_);
449  my $driver = &FileUtils::_determineDriver($path);
450  return &FileUtils::_callFunction($driver, 'fileTest', $path, '-d');
451}
452# /** directoryExists($path) **/
453
454# /** @function file_exists($path)
455#  *  Determine if the given file path exists on the target filesystem
456#  *  @param path - the path to the file to test
457#  *  @return true if the file exists, false otherwise
458sub fileExists
459{
460  my $path = shift(@_);
461  my $driver = &FileUtils::_determineDriver($path);
462  my $result = &FileUtils::_callFunction($driver, 'fileTest', $path, '-f');
463  return $result;
464}
465# /** fileExists(path) **/
466
467# /** @function filenameConcatenate(<rest>)
468#  */
469sub filenameConcatenate
470{
471  my $first_path_part = shift(@_);
472  my $path = '';
473  if (defined $first_path_part)
474  {
475    my $driver = &FileUtils::_determineDriver($first_path_part);
476    $path = &FileUtils::_callFunction($driver, 'filenameConcatenate', $first_path_part, @_);
477  }
478  return $path;
479}
480# /** filenameConcatenate(<rest>) **/
481
482# /**
483#  */
484sub fileSize
485{
486  my $path = shift(@_);
487  my $driver = &_determineDriver($path);
488  return &_callFunction($driver, 'fileSize', $path);
489}
490# /** fileSize() **/
491
492## @function hardLink()
493#
494sub hardLink
495{
496  my $src_file = shift(@_);
497  my $dst_file = shift(@_);
498  my $src_driver = &FileUtils::_determineDriver($src_file);
499  my $dst_driver = &FileUtils::_determineDriver($dst_file);
500  # you can only symbolic link within the same file system - always
501  if ($src_driver eq 'LocalFS' && $src_driver eq $dst_driver)
502  {
503    &FileUtils::_callFunction($src_driver, 'linkFile', 'HARD', $src_file, $dst_file);
504  }
505  # substitute a copy
506  elsif ($src_driver ne 'LocalFS')
507  {
508    &printWarning('Cannot symbolic link on non-local file systems - copying instead: ' . $src_file . ' => ' . $dst_file);
509    &transferFiles($src_file, $dst_file, 'COPY');
510  }
511  else
512  {
513    &printWarning('Cannot symbolic link between file systems - copying instead: ' . $src_file . ' => ' . $dst_file);
514    &transferFiles($src_file, $dst_file, 'COPY');
515  }
516}
517## hardLink()
518
519
520## @function isFilenameAbolsute()
521#
522# Determine if the given path is an absolute path (as compared to relative)
523#
524sub isFilenameAbsolute
525{
526  my $path = shift(@_);
527  my $driver = &FileUtils::_determineDriver($path);
528  return &FileUtils::_callFunction($driver, 'isFilenameAbsolute', $path, '-l');
529}
530## isFilenameAbsolute()
531
532
533## @function isSymbolicLink()
534#
535# Determine if the given path is a symbolic link
536#
537sub isSymbolicLink
538{
539  my $path = shift(@_);
540  my $driver = &FileUtils::_determineDriver($path);
541  return &FileUtils::_callFunction($driver, 'fileTest', $path, '-l');
542}
543## isSymbolicLink()
544
545# /** @function makeAllDirectories
546#  */
547sub makeAllDirectories
548{
549  my $path = shift(@_);
550  my $driver = &FileUtils::_determineDriver($path);
551  return &FileUtils::_callFunction($driver, 'makeAllDirectories', $path);
552}
553# /** makeAllDirectories() **/
554
555# /**
556#  */
557sub makeDirectory
558{
559  my $path = shift(@_);
560  my $driver = &FileUtils::_determineDriver($path);
561  # check if the directory already exists - in which case our job is done :)
562  my $result = &FileUtils::_callFunction($driver, 'fileTest', $path, '-d');
563  # not yet - better try and create it then
564  if (!$result)
565  {
566    $result = &FileUtils::_callFunction($driver, 'makeDirectory', $path);
567  }
568  return $result;
569}
570# /** makeDirectory(path) **/
571
572# /**
573#  */
574sub modificationTime
575{
576  my $path = shift(@_);
577  my $driver = &_determineDriver($path);
578  return &_callFunction($driver, 'modificationTime', $path);
579}
580# /** modificationTime() **/
581
582# /**
583#  */
584sub moveFiles
585{
586  return &transferFiles(@_, 'MOVE');
587}
588# /** moveFiles() **/
589
590# /**
591#  */
592sub openFileHandle
593{
594  my $path = shift(@_);
595  # I'll set mode to read by default, as that is less destructive to precious
596  # files on your system...
597  my $mode = shift(@_) || '<';
598  # the all important determining of the driver by protocol
599  my $driver = &FileUtils::_determineDriver($path);
600  # the function call will return true on success, with the reference to the
601  # file handle hopefully populated with a lovely file descriptor
602  return &FileUtils::_callFunction($driver, 'openFileHandle', $path, $mode, @_);
603}
604# /** openFileHandle($file_handle_ref, $path, $mode) **/
605
606## @function pathExists()
607#
608# Determine if a certain path exists on the file system - regardless of whether
609# it is a file or a directory (the equivalent of -e)
610#
611sub pathExists
612{
613  my $path = shift(@_);
614  my $driver = &FileUtils::_determineDriver($path);
615  return &FileUtils::_callFunction($driver, 'fileTest', $path, '-e');
616}
617## pathExists()
618
619
620## @function readDirectory()
621#
622# Provide a function to return the files within a directory that is aware
623# of protocols other than file://
624# @param $dirname  the full path to the directory
625#
626sub readDirectory
627{
628  my $path = shift(@_);
629  my $driver = &FileUtils::_determineDriver($path);
630  return &FileUtils::_callFunction($driver, 'readDirectory', $path);
631}
632## readDirectory()
633
634
635## @function removeFiles()
636#
637# Removes files (but not directories)
638# @param files - An array of filepaths to remove
639#
640sub removeFiles
641{
642  my (@files) = @_;
643  my $num_removed = 0;
644  # Remove the files
645  foreach my $path (@files)
646  {
647    my $driver = &FileUtils::_determineDriver($path);
648    if (&FileUtils::_callFunction($driver, 'removeFiles', $path))
649    {
650      $num_removed++;
651    }
652  }
653  # Check to make sure all of them were removed
654  if ($num_removed != scalar(@files))
655  {
656    &printError('Not all files were removed');
657    $num_removed = 0;
658  }
659  return $num_removed;
660}
661## removeFile(files)
662
663
664## @function removeFilesFiltered()
665#
666sub removeFilesFiltered
667{
668  my $maybe_paths = shift(@_);
669  my @paths = (ref $maybe_paths eq "ARRAY") ? @$maybe_paths : ($maybe_paths);
670  my $num_removed = 0;
671  foreach my $path (@paths)
672  {
673    my $driver = &FileUtils::_determineDriver($path);
674    $num_removed += &FileUtils::_callFunction($driver, 'removeFilesFiltered', $path, @_);
675  }
676  return $num_removed;
677}
678## removeFilesFiltered()
679
680
681## @function removeFilesRecursive()
682#
683# The equivalent of "rm -rf" with all the dangers therein
684#
685sub removeFilesRecursive
686{
687  my $maybe_paths = shift(@_);
688  my @paths = (ref $maybe_paths eq "ARRAY") ? @$maybe_paths : ($maybe_paths);
689  my $num_removed = 0;
690  foreach my $path (@paths)
691  {
692    my $driver = &FileUtils::_determineDriver($path);
693    $num_removed += &FileUtils::_callFunction($driver, 'removeFilesRecursive', $path);
694  }
695  return $num_removed;
696}
697## removeFilesRecursive()
698
699
700## @function softLink()
701#
702sub softLink
703{
704  my $src_file = shift(@_);
705  my $dst_file = shift(@_);
706  my $src_driver = &FileUtils::_determineDriver($src_file);
707  my $dst_driver = &FileUtils::_determineDriver($dst_file);
708  # you can only symbolic link within the same file system - always
709  if ($src_driver eq 'LocalFS' && $src_driver eq $dst_driver)
710  {
711    &FileUtils::_callFunction($src_driver, 'linkFile', 'SOFT', $src_file, $dst_file, @_);
712  }
713  # substitute a copy
714  elsif ($src_driver ne 'LocalFS')
715  {
716    &printWarning('Cannot symbolic link on non-local file systems - copying instead: ' . $src_file . ' => ' . $dst_file);
717    &transferFiles($src_file, $dst_file, 'COPY');
718  }
719  else
720  {
721    &printWarning('Cannot symbolic link between file systems - copying instead: ' . $src_file . ' => ' . $dst_file);
722    &transferFiles($src_file, $dst_file, 'COPY');
723  }
724}
725## softLink()
726
727
728## @function supportsSymbolicLink()
729#
730sub supportsSymbolicLink
731{
732  my $path = shift(@_);
733  my $driver = &FileUtils::_determineDriver($path);
734  &FileUtils::_callFunction($driver, 'supportsSymbolicLink');
735}
736## supportsSymbolicLink()
737
738
739## @function synchronizeDirectory()
740#
741sub synchronizeDirectory
742{
743  my $fromdir = shift(@_);
744  my $driver = &FileUtils::_determineDriver($fromdir);
745  &FileUtils::_callFunction($driver, 'synchronizeDirectory', $fromdir, @_);
746}
747## synchronizeDirectory()
748
749
750## @function transferFiles()
751# @param paths - one or more source paths
752# @param dst_path - the destination path
753# @param mode - copy or move
754#
755sub transferFiles
756{
757  my $transfer_mode = pop(@_);
758  my $dst_path = pop(@_);
759  my (@src_paths) = @_;
760  &_prettyPrint(0, @src_paths, $dst_path, $transfer_mode) unless (!$debug);
761  my $result = 0;
762  my $dst_driver = &_determineDriver($dst_path);
763  if (scalar (@src_paths) == 0)
764  {
765    &printError('No destination directory given');
766  }
767  elsif ((scalar (@src_paths) > 1) && (!&directoryExists($dst_path)))
768  {
769    &printError('If multiple source files are given the destination must be a directory');
770  }
771  else
772  {
773    foreach my $src_path (@src_paths)
774    {
775      my $src_driver = &_determineDriver($src_path);
776      if ($src_driver eq 'LocalFS')
777      {
778        # Local to local
779        if ($dst_driver eq 'LocalFS')
780        {
781          $result += &_callFunction($src_driver, 'transferFile', $transfer_mode, $src_path, $dst_path);
782        }
783        # Local to X
784        else
785        {
786          $result += &_callFunction($dst_driver, 'transferFileFromLocal', $transfer_mode, $src_path, $dst_path);
787        }
788      }
789      # X to Local
790      elsif ($dst_driver eq 'LocalFS')
791      {
792        $result += &_callFunction($src_driver, 'transferFileToLocal', $transfer_mode, $src_path, $dst_path);
793      }
794      # X to X
795      elsif ($src_driver eq $dst_driver)
796      {
797        $result += &_callFunction($src_driver, 'transferFile', $transfer_mode, $src_path, $dst_path);
798      }
799      # X to Y... not supported
800      else
801      {
802        &printError('transfer between two non-local file systems not supported');
803      }
804    }
805    $result = (scalar(@src_paths) == $result);
806  }
807  return $result;
808}
809## transferFiles()
810
8111;
Note: See TracBrowser for help on using the browser.