#!/usr/bin/env perl use Getopt::Std; # Options ############################################################# # -h ................ pipe man page to less (and exit). # -H ................ print man page without using less. # -v ................ print version number and exit. # -e ................ echo directory listing to standard output. # -m ................ include last modification time in listing. # -o outfile ........ print listing of files to outfile instead of # dir.lst # -d dirlist ........ compare a given directory with dirlist and # list the differences # Process Options ##################################################### getopts('hHvemo:d:'); $opt_v && do { print "Mirrorls: version 1.3 [1999/06/01]\n";exit;}; $opt_h && do { system("mirrorls -H |less"); exit; }; # Help! Pipe man page to less. $opt_H && do { write;exit;}; # Help! Print man page without using less. $opt_m && do {require 'ctime.pl';}; ######################################################################## $dir = (@ARGV) ? shift(@ARGV) : '.'; if ($opt_d ne "") { &makehash(); $dofile = "diffhash"; chdir $dir; &dodir('.'); } else { $outfile = ($opt_o) ? $opt_o : &dir($dir).".lst"; (-e $outfile) && do {print "$outfile exists. Overwrite? (y/N): "; chomp($choice = ); $choice .= "n"; die "Ok ... $outfile not overwritten. ". "Exiting.\n" unless $choice =~ /^y/i;}; open(OUT,">$outfile") || die "Cannot open $outfile for writing: $!\n"; $dofile = "prnodot".($opt_m ? "mtime" : $opt_e ? "echo" : ""); chdir $dir; &dodir('.'); close OUT; print "Directory listing printed to: $outfile\n"; } #################################################################### sub dir { my($dir) = @_; chomp($dir = `pwd`) if $dir eq '.'; $dir =~ s#.*/##; return $dir; } sub makehash { open(IN, "<$opt_d") || die "Cannot open $opt_d: $!\n"; %dirlist = (); while () { chomp; @words = split; $dirlist{$words[0]} = $words[1]; } close IN; } sub diffhash { my($dir, $name, $size) = @_; $name = "$dir/$name"; $name =~ s#\./##; if (defined($dirlist{$name})) { if ($dirlist{$name} != $size) { print "File $name differs ... Remote size: $dirlist{$name},", " Local size: $size\n"; } delete $dirlist{$name}; } elsif ($name =~ m#/$#) { chop($name); print "Extra directory: $name\n"; } elsif ($name ne $opt_d) { print "Extra file: $name ($size bytes)\n"; } } sub missing { my($dir) = @_; $dir =~ s#\./##; my(@keys) = sort grep(/^$dir/, keys(%dirlist)); foreach $name (@keys) { $size = $dirlist{$name}; ($name =~ m#/$#) ? do {chop($name); print "Missing directory: $name\n";} : print "Missing file: $name ($size bytes)\n"; delete $dirlist{$name}; } } sub prnodot { my($dir,$name,$size) = @_; $name = "$dir/$name"; $name =~ s/\.\///; print "$name has undefined size\n" if !defined($size); print OUT "$name $size\n"; } sub prnodotecho { my($dir,$name,$size) = @_; $name = "$dir/$name"; $name =~ s/\.\///; print "$name has undefined size\n" if !defined($size); print OUT "$name $size\n"; print "$name $size\n"; } sub prnodotmtime { my($dir,$name,$size) = @_; my($modtime) = (stat($name))[9]; $modtime = &ctime($modtime); $name = "$dir/$name"; $name =~ s/\.\///; print "$name has undefined size\n" if !defined($size); print OUT "$name $size $modtime"; print "$name $size $modtime" if $opt_e; } sub dodir{ local($dir,$nlink) = @_; local($dev,$ino,$mode,$subcount); # At top level we need to find nlink ourselves. ($dev,$ino,$mode,$nlink) = stat('.') unless $nlink; # Get the list of files in current directory. opendir(DIR,'.') || die "Can't open $dir: $!\n"; local(@filenames) = sort readdir(DIR); closedir(DIR); if ($nlink == 2) { # This dir has no subdirectories. for (@filenames) { next if $_ eq '.'; next if $_ eq '..'; &$dofile($dir, $_, -s $_ || 0); } } else { # This dir has subdirectories. $subcount = $nlink - 2; for (@filenames) { next if $_ eq '.'; next if $_ eq '..'; do { &$dofile($dir, $_, -s $_ || 0); next; } if $subcount == 0; # Once subcount is 0 remaining # filenames are all files ... # no subdirectories left. # If we are still going ... we get the link count # to check current array element is a directory. ($dev,$ino,$mode,$nlink) = lstat($_); do { &$dofile($dir, $_, -s $_ || 0); next; } unless -d _; # If we exit here, it was a file. &$dofile($dir, "$_/", 0); chdir $_ || die "Can't cd to $dir/$_: $!\n"; &dodir("$dir/$_",$nlink); chdir '..'; --$subcount; } } &missing("$dir/") unless $opt_d eq ""; } # Manpage ############################################################## format STDOUT = mirrorls - compile directory listing or compare mirror sites SYNOPSIS mirrorls [-hHve] [-o outfile] [-d dirlist] [dir] DESCRIPTION If called with the -h option mirrorls pipes this manpage to less. With the -H option this manpage is printed with- out piping through less. The -v option prints the version number. The essential idea is that one performs mirrorls [-em] [-o outfile] [dir] at one site to compile a listing of the files in the dir- ectory dir. If dir is omitted the current directory is assumed. Mirrorls recursively descends through subdirect- ories if necessary. The listing is printed to dir.lst unless the -o option is used, in which case the listing is directed to outfile. The listing is also echoed to standard output if the -e option is used. The -m option includes the last modification times of files/directories in the listing. Then one exports the directory listing dir.lst or outfile to a mirror site one wishes to compare a site with. At the alternative site one performs mirrorls -d dirlist [dir] where dirlist is the exported directory listing dir.lst or outfile, to do a comparison of the former site with the contents of dir. If the argument dir is omitted, `.' is assumed. Directories and links are listed as having 0 byte sizes, but, directory names are listed with trailing slashes. OPTIONS -h Print this man page, by piping to less. -H Print this man page, but do not pipe to less. -v Print version number and exit. -e Ignored if -d option used. Echo directory listing to standard output, as it is printed to dir.lst. -m Include modification times in listing. -o outfile Ignored if -d option used. Direct output listing to outfile instead of dir.lst. -d dirlist Using the remote site listing dirlist list the differences between the local and remote mirror sites. The extra fields one gets in a -m listing are ignored. Only filename and byte-size are used in determining file differences. ACKNOWLEDGEMENT The subroutine dodir is based on an example on pp56-57 of `Programming Perl' by Larry Wall and Randal L. Schwartz (1991). CHANGES Version 1.0: 1999/05/05: First release. Version 1.1: 1999/05/13: Fixed a typo. that caused slower than expected behaviour. Version 1.2: 1999/05/27: Added -m option to include mod- ification times in listing. Version 1.3: 1999/06/01: Directories are now read in sorted order. Adds a trailing slash to directory names. Missing files are listed in sorted order. BUGS Mirrorls is only known to work properly with Perl 5.001 or better. Directories and links are listed as hav- sizes of 0 bytes. AUTHOR Greg Gamble @<<<<<<<<<<<<<<<<<<<<<<<<< "" VERSION 1.3 1 June 1999 1 .