# Blosxom Plugin: copyfiles # Author(s): Simon Fraser # Version: 2004-05-15 # Documentation: when running blosxom in static mode, look for files called # "fileslist" (configurable), and copy files or directories # listed therein to the equivalent place in the destination # directory hierarchy. # # 'fileslist' files contain a simple list of files or directories, # for example (this should be left-aligned without comments): # # blog.css # scripts/testscript.js # images/ # docs/test/ # # In this case "blog.css" is a simple file. testscript.js lives # in a subdirectory, which will be created in the destination. # "images/" and "docs/test/" are directories, and will be copied # with their contents to the destination # package copyfiles; # --- Configurable variables ----- $list_filename = "fileslist"; my $noisy = 0; # -------------------------------- use File::Copy; use File::Find; use File::Path; use File::Basename; use File::stat; # -------------------------------- # used to pass data to copy routine my $cur_src, $cur_dest; # calling File::Find recursively is bad (very bad). So we have to store an array # of hashes, listing directories to copy (with src and dest) my @copy_dirs; sub start { # we only make sense in static mode return $blosxom::static_or_dynamic eq 'dynamic' ? 0 : 1; } sub end { find(\&find_filelist, $blosxom::datadir); do_copy_dirs(); 1; } sub find_filelist() { my($filename) = $_; if ($filename eq $list_filename) { my($cur_dir) = $File::Find::dir; open(FILESLIST, $File::Find::name) || die "Failed to open file $File::Find::name: $!\n"; while () { my($line) = $_; chomp($line); # three cases here: # 1. file name # 2. directory name with trailing / # 3. relative path to file or dir if ($line =~ m/\/$/) { my($src_file) = $File::Find::dir."/".$line; my($dst_file) = $src_file; $dst_file =~ s/^$blosxom::datadir/$blosxom::static_dir/; append_copy_dir($src_file, $dst_file); } else { my($src_file) = $File::Find::dir."/".$line; my($dst_file) = $src_file; $dst_file =~ s/^$blosxom::datadir/$blosxom::static_dir/; mkpath(dirname($dst_file), 0, 0777); # ensure dest dir exists if ($src_file eq $dst_file) { die "Copying a file over itself will empty it\n"; } if ($noisy) { print "copyfiles: copying file $src_file to $dst_file\n"; } copy($src_file, $dst_file) || die "Failed to copy $src_file to $dst_file: $!\n"; } } close(FILESLIST); } } sub append_copy_dir($$) { my($srcdir, $dstdir) = @_; push @copy_dirs, { "src" => $srcdir, "dest" => $dstdir }; } sub do_copy_dirs() { for $i (0 .. $#copy_dirs) { my $srcdir = $copy_dirs[$i]{"src"}; my $dstdir = $copy_dirs[$i]{"dest"}; recursive_copy($srcdir, $dstdir) || die "Failed to copy $srcdir to $dstdir: $!\n"; } } sub recursive_copy($$) { my($src, $dest) = @_; $cur_src = $src; $cur_dest = $dest; if ($noisy) { print "copyfiles: copying directory $cur_src to $cur_dest\n"; } find(\©_one_file, $src); return 1; } sub copy_one_file() { my $src_path = $File::Find::name; my $rel_path = $src_path; if ($rel_path =~ s/^$cur_src//) { my $destpath = $cur_dest.$rel_path; mkpath(dirname($destpath), 0, 0777); # ensure dest dir exists if (!-d $src_path) { if ($src_path eq $destpath) { die "Copying a file over itself will truncate it\n"; } my($src_st) = stat($src_path) or die "Failed to stat $src_path: $!\n"; # compare date and size to see if we need to copy the file if (-f $destpath) { my($dst_st) = stat($destpath) or die "Failed to stat $destpath: $!\n"; if (($src_st->mtime == $dst_st->mtime) && ($src_st->size == $dst_st->size)) { if ($noisy) { print "copyfiles: skipping $src_path: file unchanged\n"; } return; } } print "copyfiles: copying file $src_path to $destpath\n"; copy($src_path, $destpath) || die "Failed to copy $src_path to $destpath: $!\n"; # copy the timestamp over, because copy doesn't (suckage) utime(time(), $src_st->mtime, $destpath); } } } 1;