# even if file2 is a link to a directory. This option implies -f.
use Cwd;
+use POSIX; # for opendir and friends
$usage =
"usage: lns [flags] srcfile destfile\n".
"where: -a create symlinks with absolute path names\n".
" -f overwrite existing symlink at target location\n".
" -F like -f, but works even if target is link to dir\n".
+ " -r recursively construct a directory tree which\n".
+ " mirrors the source, with symlinks to all files\n".
" -v verbosely log activity (repeat for more verbosity)\n".
" -q suppress error messages on failure\n".
" also: lns --version report version number\n" .
"CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" .
"SOFTWARE.\n";
-$abs=$force=$quiet=$verbose=$FILE=0;
+$abs=$force=$quiet=$verbose=$recurse=$FILE=0;
while ($_=shift @ARGV) {
last if /^--$/;
unshift (@ARGV, $_), last unless /^-(.*)/;
if ($opt eq "a") { $abs=1; }
elsif ($opt eq "f") { $force=1; }
elsif ($opt eq "q") { $quiet=1; }
+ elsif ($opt eq "r") { $recurse=1; }
elsif ($opt eq "v") { $verbose++; }
elsif ($opt eq "F") { $force=$FILE=1; }
else { die "lns: unrecognised option '-$1'\n"; }
# name we will reference the source by.
$sourcename = $abs ? $source : &relname($source, $target);
+ my $donothing = 0;
+ my $recursing = $recurse && -d $source;
+ my $ok;
# If the target exists...
if (-e $target || readlink $target) {
- # If it's a symlink and we're in Force mode, remove it and carry on.
- if ($force && readlink $target) {
+ if ($recursing && -d $target) {
+ # If it's a directory and we're in recursive mode, just do nothing
+ # and work around it.
+ $donothing = 1;
+ } elsif ($force && readlink $target) {
+ # If it's a symlink and we're in Force mode, remove it and carry on.
unlink $target || die "lns: unable to remove link $target\n";
# Report that if in Verbose mode.
warn "lns: removing existing target link $target\n" if $verbose;
}
}
- # Make the link.
- warn "lns: linking $source: $target -> $sourcename\n" if $verbose;
- symlink($sourcename, $target) || die "lns: unable to make link to $target\n";
+ if ($recursing) {
+ # Make the directory.
+ if ($donothing) {
+ warn "lns: directory $target already exists, no need to create it\n"
+ if $verbose;
+ $ok = 1;
+ } else {
+ warn "lns: making directory $target\n"
+ if $verbose;
+ if (mkdir $target) {
+ $ok = 1;
+ } else {
+ warn "lns: unable to make directory '$target': $!\n";
+ }
+ }
+ # Now recurse into it.
+ if ($ok) {
+ my $dh = POSIX::opendir($source);
+ my @files = POSIX::readdir($dh);
+ my $f;
+ POSIX::closedir($dh);
+ foreach $f (@files) {
+ next if $f eq "." or $f eq "..";
+ &makelink("$source/$f", "$target/$f");
+ }
+ }
+ } else {
+ # Make the link.
+ warn "lns: linking $source: $target -> $sourcename\n" if $verbose;
+ symlink($sourcename, $target) || die "lns: unable to make link to $target\n";
+ }
}
sub normalise {
\cw{cp}; and \cw{lns} will figure out for itself that the literal
text of the symlink needs to be \c{../hello.c}.
+\cw{lns} also has a mode in which it will create a symlink mirror of
+an entire directory tree: that is, instead of creating a single
+symlink to the root of the tree, it will create \e{directories} in
+the same structure as the whole of the original tree, and fill them
+with individual symlinks to the files. This is occasionally handy if
+you want to work with a slightly modified version of a large file
+hierarchy but you don't want to waste the disk space needed to
+create an entirely separate copy: you can symlink-mirror the whole
+tree, and then just replace one or two of the symlinks with modified
+versions of the files they point to.
+
\U ARGUMENTS
If you provide precisely two arguments to \cw{lns}, and the second
destination directory. This option is useful for overriding an
existing link to one directory with a link to a different one.
+\dt \cw{-r}
+
+\dd Enables recursive link-tree construction. If the source pathname
+exists and is a directory, then instead of creating a symlink to it
+at the target site, \cw{lns} will create a fresh directory, and then
+recursively attempt to link every file inside the source directory
+to the inside of the new target directory.
+
+\lcont{
+
+If a directory already
+exists at the target site, \cw{lns} will recurse into it; so you
+can, for instance, use \cw{lns -r -f} to refresh an existing link
+tree.
+
+}
+
\dt \cw{-v}
\dd Verbose mode: makes \cw{lns} talk about what it is doing. You
overwriting. So now you get what you wanted: the previous symlink
\cw{subdir3} is replaced with one whose link text reads \cq{subdir}.
+Next, a couple of examples with \cw{-r}. Suppose you have your
+subdirectory \cw{subdir}. Then running
+
+\c $ lns -r subdir subdir-mirror
+\e bbbbbbbbbbbbbbbbbbbbbbbbbbb
+
+will create a new subdirectory called \c{subdir-mirror}, containing
+symlinks to everything in \c{subdir}.
+
+If the directory \c{subdir-mirror} already existed, however,
+\cw{lns}'s command-line processing will notice that it's a
+directory, and will assume things are supposed to be copied \e{into}
+it, so that your mirror of \c{subdir} will end up at
+\c{subdir-mirror/subdir}. To fix this, you can again use \cw{-F}, to
+tell \cw{lns} to literally create its output at the precise location
+you specify rather than inside it:
+
+\c $ lns -rF subdir subdir-mirror
+\e bbbbbbbbbbbbbbbbbbbbbbbbbbbb
+
\U LICENCE
\cw{lns} is free software, distributed under the MIT licence. Type