home *** CD-ROM | disk | FTP | other *** search
- #! /usr/local/bin/perl
-
- # Subroutine library for common file related actions.
-
- {
- package main;
- require 'stat.pl';
- require 'mrm-export.pl';
-
- &export ('files', $EXPORT_FUNCTION,
- 'rename_file',
- 'change_mode',
- 'make_directory',
- 'delete_file',
- 'copy_file',
- 'compare_files');
-
- &import ('files', $IMPORT_FUNCTION, 'trace', 'fix_file', 'unfix_file');
- &import ('files', $IMPORT_VARIABLE, 'trace_p');
-
- $EXPORT_FUNCTION || $IMPORT_FUNCTION || $IMPORT_VARIABLE; # silence perl -w
- }
-
- package files;
- $bufsize = 16384;
- $prog = substr ($0, rindex ($0, '/') + 1);
-
-
- # Function to rename a file, trace the result, and die if an error occurred.
-
- sub rename_file {
- local ($nargs) = scalar (@_);
- die "$prog: rename_file called with $nargs arguments.\n" if ($nargs != 2);
-
- local ($old, $new) = @_;
-
- &delete_file ($new) if (-e $new);
- &trace ('rename', $old, $new) if ($trace_p);
- rename ($old, $new) || die "$prog: rename $old $new: $!\n";
- 1;
- }
-
-
- # Function to change the mode on one or more files, and die if there was any errors
-
- sub change_mode {
- local ($nargs) = scalar (@_);
- die "$prog: change_mode called with $nargs arguments.\n" if ($nargs < 2);
-
- local ($mode, @files) = @_;
- local ($mode_octal) = sprintf ("0%03o", $mode);
- local (@errors) = ();
-
- foreach (@files) {
- &trace ('chmod', $mode_octal, $_) if ($trace_p);
- chmod ($mode, $_) || push (@errors, "$prog: chmod $mode_octal $_: $!\n");
- }
-
- die join ('', @errors) if (scalar (@errors) > 0);
- 1;
- }
-
-
- # Function to make a directory, trace the result, and die if an error occurred.
- # If any parent directories also don't exist, they are made too.
-
- sub make_directory {
- local ($nargs) = scalar (@_);
- die "$prog: make_directory called with $nargs arguments.\n" if ($nargs == 0 || $nargs > 2);
-
- local ($name) = shift (@_);
- local ($mode) = (scalar (@_) > 0) ? shift (@_) : 0755;
- local ($pmode) = sprintf ("0%03o", $mode);
- local (@pieces) = split ('/', $name);
- local ($dir) = undef;
- local ($i);
-
- for ($i = $[; $i <= $#pieces; $i++) {
- $dir = join ('/', @pieces[ $[..$i ]);
- next if ($dir eq '');
-
- if (! -d $dir) {
- &trace ("mkdir", $dir, $pmode) if ($trace_p);
- mkdir ($dir, $mode) || die "$prog: mkdir $dir $pmode: $!\n";
- }
- }
-
- 1;
- }
-
-
- # Function to delete one or more files.
- # If any of the of files is a directory, delete all of it's contents.
- # Return value is the number of deletes that failed.
-
- sub delete_file {
- local (@files) = @_;
- local (@dirstack) = ();
- local ($ret) = 0;
- local ($ret2);
- local ($file);
- local ($subfile);
-
- while ($file = shift (@files)) {
- if (-l $file || (-e _ && !-d _)) {
- &trace ('unlink', $file) if ($trace_p);
- $ret2 = unlink ($file);
- if (!$ret2) {
- $ret++;
- warn "$prog: unlink $file: $!\n";
- }
-
- } elsif (-d _) {
- &change_mode (0777, $file) if (! -w _);
-
- &trace ('opendir', $file) if ($trace_p);
- opendir (DIR, $file) || warn "$prog: opendir $file: $!\n";
-
- while ($subfile = readdir (DIR)) {
- next if ($subfile eq '.' || $subfile eq '..');
- push (@files, "$file/$subfile");
- }
-
- &trace ('closedir', $file) if ($trace_p);
- closedir (DIR);
-
- unshift (@dirstack, $file);
- }
- }
-
- foreach $dir (@dirstack) {
- &trace ('rmdir', $dir) if ($trace_p);
- $ret2 = rmdir ($dir);
- if (!$ret2) {
- $ret++;
- warn "$prog: rmdir $dir: $!\n";
- }
- }
-
- $ret;
- }
-
-
- # Function to copy a file
- # Arg 1 is the destination file
- # Arg 2 is the source file
- # Arg 3 is the mode, if not passed, use the source file mode
- # Arg 4 is true if the file should not be deleted first in doing the copy.
- # Arg 5 is a bitmask to OR the mode with (to add permission bits)
- # Arg 6 is a bitmask to AND the mode with (to remove permission bits)
- # Arg 7 is true if we should copy a symlink'ed file instead of propigating the symlink.
-
- sub copy_file {
- local ($nargs) = scalar (@_);
- die "$prog: copy_file called with $nargs arguments.\n" if ($nargs < 2 || $nargs > 7);
-
- local ($package, $csrc, $line) = caller;
- local ($dest) = shift (@_);
- local ($src) = shift (@_);
- local ($mode) = shift (@_);
- local ($no_delete) = shift (@_);
- local ($mode_or) = shift (@_);
- local ($mode_and) = shift (@_);
- local ($no_symlink) = shift (@_);
- local ($dev, $inode);
-
- $no_delete = 0 if (! defined ($no_delete));
- &delete_file ($dest) if (-e $dest && !$no_delete);
-
- if (-l $src && !$no_symlink) {
- local ($link) = readlink ($src);
- &trace ('ln', '-s', $link, $dest) if ($trace_p);
- symlink ($link, $dest) || die "$prog: ln -s $link $dest_file: $!\n";
-
- } elsif (-d _ || (-l _ && -d $src)) {
- local (@files) = ();
- local ($src_file) = undef;
- local ($dest_file) = undef;
-
- if (! defined ($mode)) {
- local ($dev, $inode);
-
- ($dev, $inode, $mode) = stat (_);
- $mode &= 07777;
- }
-
- $mode |= $mode_or if (defined ($mode_or));
- $mode &= $mode_and if (defined ($mode_and));
-
- # Copy all files into a local array, so that the DIR file handle doesn't get
- # clobbered if we are handling an entire directory tree.
- # Also we don't deal with linked files in the directory at this time.
-
- &make_directory ($dest, 0700);
- &trace ('opendir', $src) if ($trace_p);
- opendir (DIR, $src) || die "$prog: opendir $src: $!\n";
- @files = grep ($_ ne '.' && $_ ne '..', readdir (DIR));
- &trace ('closedir', $src) if ($trace_p);
- closedir (DIR);
-
- foreach $file (@files) {
- $src_file = join ('/', $src, $file);
- $dest_file = join ('/', $dest, $file);
- ©_file ($dest_file, $src_file, undef, $no_delete, $mode_or, $mode_and, $no_symlink);
- }
-
- &change_mode ($mode, $dest);
-
- } else {
- local (@stat) = stat (_); # stat done above for -d/-l
- local ($file_bufsize) = ($stat[$main'ST_BLKSIZE] > $bufsize) ? $stat[$main'ST_BLKSIZE] : $bufsize;
- local ($dest_fix) = &fix_file ($dest, '>', $package);
- local ($src_fix) = &fix_file ($src, '<', $package);
- local ($len) = undef;
- local ($buf) = "\0" x $file_bufsize;
-
- if (! defined ($mode)) {
- local ($dev, $inode);
-
- ($dev, $inode, $mode) = stat (_);
- $mode &= 07777;
- }
-
- $mode |= $mode_or if (defined ($mode_or));
- $mode &= $mode_and if (defined ($mode_and));
-
- open (INP, $src_fix) || die "$prog: open $src: $!\n";
- open (OUT, $dest_fix) || die "$prog: open $dest: $!\n";
-
- &trace (sprintf ("copy [mode 0%03o] %s %s%s",
- $mode, $src, $dest,
- (($no_delete) ? ', no delete' : ''))) if ($trace_p);
-
- while ($len = sysread (INP, $buf, $file_bufsize)) {
- if (! defined ($len)) {
- next if ($! =~ /^Interrupted/);
- die "$prog: read error: $!\n";
- }
-
- $offset = 0;
- while ($len) {
- $written = syswrite (OUT, $buf, $len, $offset);
- die "$prog: write error: $!\n" unless defined ($written);
-
- $len -= $written;
- $offset += $written;
- }
- }
-
- close (INP);
- close (OUT);
-
- &change_mode ($mode, $dest);
- }
- }
-
-
- # Function to compare two files, returning 0 if they are equal, != 0 otherwise.
- # Argument 1 is a file to compare.
- # Argument 2 is a file to compare.
- # Argument 3 is an optional timestamp to assume the files are equal if both older than it
-
- # Taken from dcmp, but hacked upon since then.
-
- sub compare_files {
- local ($nargs) = scalar (@_);
- die "$prog: compare_files called with $nargs arguments.\n" if ($nargs < 2 || $nargs > 3);
-
- local ($package, $src, $line) = caller;
- local ($file_a) = &fix_file (shift (@_), '', $package);
- local ($file_b) = &fix_file (shift (@_), '', $package);
- local ($last_change_time) = shift (@_);
- local ($ret) = 0;
- local ($done) = 0;
- local ($len_a, $len_b);
- local ($buf_a, $buf_b);
- local (@stat_a);
- local (@stat_b);
- local ($len_a);
- local ($len_b);
- local ($bufsize_a);
- local ($bufsize_b);
- local ($file_bufsize);
-
- &trace ('compare', $file_a, $file_b) if ($trace_p);
-
-
- if (! (@stat_a = stat ($file_a))) {
- $ret = 2;
- $done = 1;
- &trace ("stat error: $file_a: $!") if ($trace_p);
-
- } elsif (! (@stat_b = stat ($file_b))) {
- $ret = 2;
- $done = 1;
- &trace ("stat error: $file_b: $!") if ($trace_p);
- }
-
- $len_a = $stat_a[ $[ + $main'ST_SIZE];
- $len_b = $stat_b[ $[ + $main'ST_SIZE];
-
- # Don't bother actually reading the file if we know it isn't equal
- if ($done) { # error above in doing stats
-
- } elsif ($len_a != $len_b) { # files have different lengths
- $ret = 1;
- $done = 1;
-
- } elsif ($len_a == 0) { # zero length files
- $ret = 0;
- $done = 1;
-
- } elsif (defined ($last_change_time) && $last_change_time
- && $stat_a[$ST_MTIME] < $last_change_time
- && $stat_b[$ST_MTIME] < $last_change_time) { # files modified before some time delta
-
- $ret = 0;
- $done = 1;
- }
-
-
- if (! $done) {
- $bufsize_a = ($stat_a[$main'ST_BLKSIZE] > $bufsize) ? $stat[$main'ST_BLKSIZE] : $bufsize;
- $bufsize_b = ($stat_b[$main'ST_BLKSIZE] > $bufsize) ? $stat[$main'ST_BLKSIZE] : $bufsize;
- $file_bufsize = ($bufsize_a > $bufsize_b) ? $bufsize_a : $bufsize_b;
-
- $buf_a = "\0" x $file_bufsize;
- $buf_b = "\0" x $file_bufsize;
-
- open (FILE_A, $file_a) || die "$prog: open $file_a: $!\n";
- open (FILE_B, $file_b) || die "$prog: open $file_b: $!\n";
-
- while (! $done) {
- $len_a = sysread (FILE_A, $buf_a, $file_bufsize);
-
- if (! defined ($len_a)) {
- next if ($! =~ /^Interrupted/);
- die "$prog: read error on $file_a: $!\n";
- }
-
- while () {
- $len_b = sysread (FILE_B, $buf_b, $file_bufsize);
- last if (defined ($len_b));
- next if ($! =~ /^Interrupted/);
- die "$prog: read error on $file_b: $!\n";
- }
-
- last if ($len_a == 0 && $len_b == 0);
- next if ($len_a == $len_b && $buf_a eq $buf_b);
-
- $ret = 1;
- $done = 1;
- }
- }
-
- &trace (($ret) ? 'files are not equal.' : 'files are equal.') if ($trace_p);
- $ret;
- }
-
-
- # Make sure require does not abort
- 1;
-