You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
569 lines
19 KiB
569 lines
19 KiB
#!/usr/bin/perl
|
|
|
|
$version="1.6.3";
|
|
# Author: Rainer Krienke, krienke@uni-koblenz.de
|
|
#
|
|
# Description:
|
|
#
|
|
# This script will create a linktree for all photos in a digikam
|
|
# database that have tags set on them. Tags are used in digikam to
|
|
# create virtual folders containing images that all have one or more tags.
|
|
# Since other programs do not know about these virtual folders this script
|
|
# maps these virtual folders into the filesystem by creating a directory
|
|
# for each tag name and then linking from the tag dir to the tagged image in the
|
|
# digikam photo directory.
|
|
#
|
|
# Call this script like:
|
|
# digitaglinktree -r /home/user/photos -l /home/user/photos/tags \
|
|
# -d /home/user/photos/digikam.db
|
|
|
|
|
|
# Changes
|
|
#
|
|
# 1.1->1.2:
|
|
# Support for hierarchical tags was added. The script can either create
|
|
# a hierarchical directory structure for these tags (default) or treat them
|
|
# as tags beeing on the same level resulting in a flat dir structure (-f)
|
|
#
|
|
# 1.2->1.3
|
|
# Added support for multiple tags assigned to one photo. Up to now only
|
|
# one tag (the last one found) was used.
|
|
#
|
|
# 1.3->1.4
|
|
# Bug fix, links with same name in one tag directory were no resolved
|
|
# which led to "file exists" errors when trying to create the symbolic link.
|
|
#
|
|
# 1.4-> 1.6, 2006/08/03
|
|
# Added an option (-A) to archive photos and tag structure in an extra directory
|
|
# 1.6-> 1.6.1 2006/08/15
|
|
# Added an option (-C) for archive mode (-A). Added more information to
|
|
# the manual page.
|
|
# 1.6.1-> 1.6.2 2008/11/24 (Peter Muehlenpfordt, muehlenp@gmx.de)
|
|
# Bug fix, subtags with the same name in different branches were merged
|
|
# into one directory.
|
|
# 1.6.2 -> 1.6.3 2008/11/25 Rainer Krienke, krienke@uni-koblenz.de
|
|
# Fixed a bug that shows in some later perl interpreter versions when
|
|
# accessing array references with $# operator. If $r is a ref to an array
|
|
# then $#{@r} was ok in the past but now $#{r] is correct, perhaps it was
|
|
# always correct and the fact that $#{@r} worked was just good luck ....
|
|
#
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# LICENSE:
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2, or (at your option)
|
|
# any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
# 02110-1301, USA.
|
|
#
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
use Getopt::Std;
|
|
use File::Spec;
|
|
use File::Path;
|
|
|
|
# Please adapt the sqlite version to your digikam version.
|
|
# Do not forget to also install the correct sqlite version:
|
|
# Digikam 0.7x needs sqlite2
|
|
# Digikam 0.8x needs sqlite3
|
|
|
|
#$SQLITE="/usr/bin/sqlite";
|
|
$SQLITE="/usr/bin/sqlite3";
|
|
$archiveDirPhotos="Photos";
|
|
$archiveDirLinks="Tags";
|
|
|
|
#
|
|
$scriptAuthor="krienke@uni-koblenz.de";
|
|
|
|
#
|
|
# check if sqlite can open the database
|
|
#
|
|
sub checkVersion{
|
|
my($database)=shift;
|
|
my(@data, $res);
|
|
|
|
if( ! -r $SQLITE ){
|
|
warn "Cannot start sqlite executable \"$SQLITE\". \n",
|
|
"Please check if the executable searched is really installed.\n",
|
|
"You should also check if the installed version is correct. The name\n",
|
|
"of the executable to search for can be set in the head of this script.\n\n";
|
|
exit(1);
|
|
}
|
|
|
|
@data=`$SQLITE $database .tables 2>&1`; # Try to get the tables information
|
|
|
|
if( $? != 0 || grep(/Error:\s*file is encrypted/i, @data) != 0 ){
|
|
warn "Cannot open database \"$database\". Have you installed a current\n",
|
|
"version of sqlite? Depending on you digikam version you have to \n",
|
|
"install and use sqlite2 (for digikam 0.7x) or sqlite3 (for digikam >= 0.8 )\n",
|
|
"Please install the correct version and set the SQLITE variable in the\n",
|
|
"script head accordingly \n\n";
|
|
exit(1);
|
|
}
|
|
|
|
$res=grep(/TagsTree/, @data);
|
|
if( $res==0 ){
|
|
return("7") # digikam version 0.7
|
|
}else{
|
|
return("8"); # digikam version 0.8
|
|
}
|
|
}
|
|
|
|
|
|
#
|
|
# Get all images that have tags from digikam database
|
|
# refPathNames will contain all photos including path having tags
|
|
# The key in refPathNames is the path, value is photos name
|
|
#
|
|
sub getTaggedImages{
|
|
my($refImg, $database, $rootDir, $refPaths, $refPathNames)=@_;
|
|
my($i, $image, $path, $tag, $status, $id, $pid, $tmp);
|
|
my($sqliteCmd, @data, $vers, @tags, %tagPath, $tagCount, $refTag);
|
|
|
|
$vers=checkVersion($database); # Find out version of digikam
|
|
|
|
# Get tag information from database
|
|
$sqliteCmd="\"select id,pid,name from tags;\"";
|
|
|
|
@data=`$SQLITE $database $sqliteCmd`;
|
|
$status=$?;
|
|
if( $status == 0 ){
|
|
foreach $i ( @data ){
|
|
chomp($i);
|
|
($id, $pid, $tag)=split(/\|/, $i);
|
|
# map tag id data into array
|
|
$tags[$id]->{"tag"}="$tag";
|
|
$tags[$id]->{"pid"}="$pid";
|
|
}
|
|
|
|
# Now build up the path for tags having parents
|
|
# Eg: a tag people might have a subtag friends: people
|
|
# |->friends
|
|
# We want to find out the complete path for the subtag. For friends this
|
|
# would be the path "people/friends". Images with tag friends would then be
|
|
# linked in the directory <tagsroot>/people/friends/
|
|
for($i=0; $i<=$#tags; $i++){
|
|
$pid=$tags[$i]->{"pid"}; # Get tqparent tag id of current tag
|
|
$tag=$tags[$i]->{"tag"}; # Start constructing tag path
|
|
if( $pid == 0 ){
|
|
$tagPath{$i}=$tag;
|
|
next;
|
|
}
|
|
|
|
while( $pid != 0){
|
|
$tag=$tags[$pid]->{"tag"} . "/$tag"; # add parents tag name to path
|
|
$pid=$tags[$pid]->{"pid"}; # look if tqparent has another tqparent
|
|
}
|
|
# Store path constructed
|
|
$tagPath{$i}=$tag;
|
|
#warn "+ $tag \n";
|
|
}
|
|
}else{
|
|
print "Error running SQL-Command \"$sqliteCmd\" on database \"$database\"\n";
|
|
}
|
|
|
|
|
|
if( $vers==7 ){
|
|
# SQL to get all tagged images from digikam DB with names of tags
|
|
$sqliteCmd="\"select ImageTags.name,Albums.Url,ImageTags.tagid from " .
|
|
" ImageTags, Albums,Tags " .
|
|
" where Albums.id = ImageTags.dirid; \"";
|
|
|
|
}elsif( $vers == 8 ){
|
|
# SQL to get all tagged images from digikam DB with names of tags
|
|
$sqliteCmd="\"select Images.name,Albums.url,ImageTags.tagid from" .
|
|
" Images,Albums,ImageTags where " .
|
|
" Images.dirid=Albums.id AND Images.id=ImageTags.imageid; \"";
|
|
}else{
|
|
warn "Unsupported digikam database version. Contact $scriptAuthor \n\n";
|
|
exit(1);
|
|
}
|
|
|
|
@data=`$SQLITE $database $sqliteCmd`;
|
|
$status=$?;
|
|
if( $status == 0 ){
|
|
foreach $i ( @data ){
|
|
chomp($i);
|
|
($image, $path, $tag)=split(/\|/, $i);
|
|
$refPaths->{"$rootDir$path"}=1;
|
|
$refPathNames->{"$rootDir$path/$image"}=1;
|
|
|
|
$tmp=$path;
|
|
#warn "- $rootDir, $path, $image \n";
|
|
# Enter all subpaths in $path as defined too
|
|
do{
|
|
$tmp=~s/\/[^\/]+$//;
|
|
$refPaths->{"$rootDir$tmp"}=1;
|
|
} while (length($tmp));
|
|
|
|
|
|
$refImg->{"$path/$image"}->{"path"}=$path;
|
|
$refImg->{"$path/$image"}->{"image"}=$image;
|
|
$refTag=$refImg->{"$path/$image"}->{"tag"};
|
|
#
|
|
# One image can have several tags assigned. So we manage
|
|
# a list of tags for each image
|
|
$tagCount=$#{$refTag}+1;
|
|
# The tags name
|
|
$refImg->{"$path/$image"}->{"tag"}->[$tagCount]=$tag;
|
|
# tags path for sub(sub+)tags
|
|
$refImg->{"$path/$image"}->{"tagpath"}->[$tagCount]=$tagPath{$tag};
|
|
}
|
|
}else{
|
|
print "Error running SQL-Command \"$sqliteCmd\" on database \"$database\"\n";
|
|
}
|
|
|
|
return($status);
|
|
}
|
|
|
|
|
|
#
|
|
# Create a directory with tag-subdirectories containing links to photos having
|
|
# tags set. Links can be absolute or relative as well as hard or soft.
|
|
#
|
|
sub createLinkTree{
|
|
my($refImages)=shift;
|
|
my($photoRootDir, $linktreeRootDir, $opt_flat, $opt_absolute, $linkMode)=@_;
|
|
my( $i, $j, $cmd, $path, $image, $tag, $qpath, $qtag, $qimage, $linkName,
|
|
$tmp, $ret, $sourceDir, $destDir);
|
|
my($qtagPath, $relPath);
|
|
|
|
if( -d "$linktreeRootDir" ){
|
|
# Remove old backuplinktree
|
|
system("rm -rf ${linktreeRootDir}.bak");
|
|
if( $? ){
|
|
die "Cannot run \"rm -rf ${linktreeRootDir}.bak\"\n";
|
|
}
|
|
|
|
# rename current tree to .bak
|
|
$ret=rename("$linktreeRootDir", "${linktreeRootDir}.bak" );
|
|
if( !$ret ){
|
|
die "Cannot rename \"$linktreeRootDir\" to \"${linktreeRootDir}.bak\"\n";
|
|
}
|
|
}
|
|
|
|
# Create new to level directory to hold linktree
|
|
$ret=mkdir("$linktreeRootDir");
|
|
if( !$ret ){
|
|
die "Cannot mkdir \"$linktreeRootDir\"\n";
|
|
}
|
|
# Iterate over all tagged images and create links
|
|
foreach $i (keys(%$refImages)){
|
|
# Check that the images in the linktrees themselves are
|
|
# ignored in the process of link creation. If we do not do this
|
|
# we might get into trouble when the linktree root is inside the
|
|
# digikam root dir and so the image links inside the liktree directory
|
|
# are indexed by digikam.
|
|
next if( -l "${photoRootDir}$i" );
|
|
|
|
$path=$refImages->{$i}->{"path"};
|
|
$image=$refImages->{$i}->{"image"};
|
|
#$qimage=quotemeta($image);
|
|
#$qpath=quotemeta($path);
|
|
|
|
# Handle all of the tags for the current photo
|
|
for( $j=0; $j<=$#{$refImages->{$i}->{"tag"}}; $j++){
|
|
$tag=$refImages->{$i}->{"tagpath"}->[$j];
|
|
#$qtagPath=quotemeta($tagPath);
|
|
|
|
# For tags that have subtags there is a path defined in qtagPath
|
|
# describing the parentrelationship as directory path like "friends/family/brothers"
|
|
# If it is defined we want to use this path instead of the flat tag name like "brothers"
|
|
# Doing so results in a directory hirachy that maps the tags tqparent relationships
|
|
# into the filesystem.
|
|
if( $opt_flat ){
|
|
# For flat option just use the last subdirectory
|
|
$tag=~s/^.+\///;
|
|
}
|
|
# Create subdirectories needed
|
|
if( ! -d "${linktreeRootDir}/$tag" ){
|
|
$ret=mkpath("${linktreeRootDir}/$tag", 0, 0755);
|
|
|
|
if( !$ret ){
|
|
die "Cannot mkdir \"${linktreeRootDir}/$tag\"\n";
|
|
}
|
|
}
|
|
# Avoid conflicts for images with the same name in one tag directory
|
|
$linkName=$image;
|
|
$tmp=$image;
|
|
while( -r "${linktreeRootDir}/$tag/$tmp" ){
|
|
$linkName="_" . $linkName;
|
|
$tmp="_" . $tmp;
|
|
}
|
|
|
|
if(length($opt_absolute)){
|
|
# Create link
|
|
$sourceDir="${photoRootDir}$path/$image";
|
|
$destDir="${linktreeRootDir}/$tag/$linkName";
|
|
}else{
|
|
# Get relative path from absolute one
|
|
# $rel=File::Spec->abs2rel( $path, $base ) ;
|
|
$relPath="";
|
|
$relPath=File::Spec->abs2rel("${photoRootDir}$path", "${linktreeRootDir}/$tag" ) ;
|
|
if( !length($relPath)){
|
|
die "Cannot create relative path from \"${linktreeRootDir}/$tag\" to \"${photoRootDir}$path\" . Abort.\n";
|
|
}
|
|
# Create link
|
|
#print "-> $relPath\n";
|
|
$sourceDir="$relPath/$image";
|
|
$destDir="${linktreeRootDir}/$tag/$linkName";
|
|
}
|
|
|
|
if( $linkMode eq "symbolic" ){
|
|
$ret=symlink($sourceDir, $destDir);
|
|
if( !$ret ){
|
|
$ret="$!";
|
|
warn "Warning: Failed symbolic linking \"$sourceDir\" to \"$destDir\": ",
|
|
"\"$ret\"\n";
|
|
}
|
|
}elsif( $linkMode eq "hard" ){
|
|
$ret=link($sourceDir, $destDir);
|
|
if( !$ret ){
|
|
$ret="$!";
|
|
warn "Warning: Failed hard linking \"$sourceDir\" to \"$destDir\": ",
|
|
"\"$ret\"\n";
|
|
}
|
|
}else{
|
|
die "$0: Illegal linkMode: \"$linkMode\". Abort.\n";
|
|
}
|
|
|
|
}# for
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#
|
|
# Clone a directory into another directory.
|
|
# source and destination have to exist
|
|
# Files in $srcDir will symbolically or hard linked
|
|
# to the $cloneDir according to $cloneMode which can
|
|
# be symbolic for symbolic links or hard for hardlinks
|
|
# refPathNames contains path for all photos having tags set
|
|
#
|
|
sub cloneDir{
|
|
local($srcDir, $cloneDir, $linkMode, $cloneMode, $refPaths, $refPathNames)=@_;
|
|
local(@entries, $ki, $path, $name, $ret);
|
|
|
|
# Read directory entries
|
|
opendir( DIR, $srcDir ) || warn "Cannot read directory $srcDir \n";
|
|
local(@entries)=readdir(DIR);
|
|
closedir(DIR);
|
|
|
|
foreach $k (@entries){
|
|
next if( $k eq '.' );
|
|
next if( $k eq '..');
|
|
next if( $k =~ /digikam.*\.db/o );
|
|
|
|
#warn "-> $srcDir, $k, $cloneDir\n";
|
|
# Recurse into directories
|
|
if( -d "$srcDir/$k" ){
|
|
# if cloneMode is "minimal" then only clone directories and files
|
|
# with photos that have tags set.
|
|
if( $cloneMode ne "minimal" ||
|
|
defined($refPaths->{"$srcDir/$k"}) ){
|
|
#warn "* $srcDir/$k defined \n";
|
|
mkdir("$cloneDir/$k", 0755);
|
|
if( $? ){
|
|
die "Cannot run \"mkdir $cloneDir/$k\"\n";
|
|
}
|
|
cloneDir("$srcDir/$k", "$cloneDir/$k", $linkMode,
|
|
$cloneMode, $refPaths, $refPathNames );
|
|
}
|
|
}else{
|
|
# if cloneMode is "minimal" then only clone directories and files
|
|
# with photos that have tags set.
|
|
if( $cloneMode ne "minimal" ||
|
|
defined($refPathNames->{"$srcDir/$k"}) ){
|
|
# link files
|
|
if( $linkMode eq "symbolic" ){
|
|
$ret=symlink( "$srcDir/$k", "$cloneDir/$k");
|
|
if( !$ret ){
|
|
$ret="$!";
|
|
warn "Warning: In cloning failed symbolic linking \"$srcDir/$k\" to \"$cloneDir/$k\": ",
|
|
"\"$ret\"\n";
|
|
}
|
|
}elsif( $linkMode eq "hard" ){
|
|
$ret=link( "$srcDir/$k", "$cloneDir/$k");
|
|
if( !$ret ){
|
|
$ret="$!";
|
|
warn "Warning: In cloning failed hard linking \"$srcDir/$k\" to \"$cloneDir/$k\": ",
|
|
"\"$ret\"\n";
|
|
}
|
|
}else{
|
|
die "$0: Illegal cloneLinkMode: $linkMode set. Aborting.\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#
|
|
# Manage cloning of a complete directory. If the clone directory exists
|
|
# a .bak will be created
|
|
# Files will be linked (hard/symbolic) according to cloneLinkMode which
|
|
# should be "symbolic" or "hard"
|
|
# cloneMode can "minimal" or anything else. minimal means only to clone
|
|
# those files and directories that have tags set. Anything else means
|
|
# to clone the whole photo tree with all files
|
|
#
|
|
sub runClone{
|
|
my($rootDir, $cloneDir, $photoDir, $cloneMode, $cloneLinkMode,
|
|
$refPaths, $refPathNames)=@_;
|
|
my($ret);
|
|
my($dev_r,$dev_A, $ino,$mode,$nlink,$uid,$gid,$rdev,$size,
|
|
$atime,$mtime,$ctime,$blksize,$blocks);
|
|
|
|
if( -d "$cloneDir" ){
|
|
# Remove old backuplinktree
|
|
system("rm -rf ${cloneDir}.bak");
|
|
if( $? ){
|
|
die "Cannot run \"rm -rf ${cloneDir}.bak\"\n";
|
|
}
|
|
|
|
# rename current tree to .bak
|
|
$ret=rename("$cloneDir", "${cloneDir}.bak" );
|
|
if( !$ret ){
|
|
die "Cannot rename \"$cloneDir\" to \"${cloneDir}.bak\"\n";
|
|
}
|
|
}
|
|
$ret=mkdir("$cloneDir", 0755);
|
|
if( !$ret ){
|
|
die "Cannot run \"mkdir $cloneDir\"\n";
|
|
}
|
|
$ret=mkdir("$cloneDir/$photoDir", 0755);
|
|
if( !$ret ){
|
|
die "Cannot run \"mkdir $cloneDir/$photoDir\"\n";
|
|
}
|
|
|
|
# Check if root and archive dir are on same filesystem
|
|
($dev_r,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
|
|
$atime,$mtime,$ctime,$blksize,$blocks) = stat($rootDir);
|
|
($dev_A,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
|
|
$atime,$mtime,$ctime,$blksize,$blocks) = stat($cloneDir);
|
|
#
|
|
if( $dev_r != $dev_A ){
|
|
warn "Error: \"$rootDir\" and \"$cloneDir\" are not on the same filesystem. Please select ",
|
|
"an archive directory on the same filesystem like digikams root directory! Abort.\n";
|
|
exit 1;
|
|
}
|
|
|
|
# Create clone of all entries in $rootDir into $clonedir
|
|
cloneDir($rootDir, "$cloneDir/$photoDir", $cloneLinkMode,
|
|
$cloneMode, $refPaths, $refPathNames);
|
|
}
|
|
|
|
|
|
#
|
|
# print out usage
|
|
#
|
|
sub usage{
|
|
print "$0 -r <photoroot> \n",
|
|
" -l <taglinkdir> | -A <archivedir> \n",
|
|
" -d <digikamdatabasefile> \n",
|
|
" [-H|-f|-a|-v|-C] \n",
|
|
" -r <rootdir> path to digikams root directory containing all photos\n",
|
|
" -l <tagdir> path to directory where the tag directory structure with \n",
|
|
" links to the original images should be created \n",
|
|
" -d <dbfile> full path to the digikam database file \n",
|
|
" -A <ardir> Create selfcontained archive of photos and tags in directory\n",
|
|
" ardir. rootdir and arDir have to be on the *same* filesystem!\n",
|
|
" -C If -A <archdir> was given this option will put hardlinks of all\n",
|
|
" photos in the \"$archiveDirPhotos\" directory not only of those with tags.\n",
|
|
" -a Create absolute symbolic links instead of relative ones \n",
|
|
" -H Use hard links instead of symbolic links in linktree. \n",
|
|
" -f If there are hierarchical tags (tags that have subtags) \n",
|
|
" create a flat tag directory hierarchy. So directories for\n",
|
|
" subtags are at the same directory level like their tqparent tags\n",
|
|
" -h Print this help \n",
|
|
" -v Print scripts version number \n";
|
|
|
|
print "Exit.\n\n";
|
|
exit 1;
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
# main
|
|
# ------------------------------------------------------------------
|
|
$ret=getopts('HhCar:l:d:A:fv');
|
|
|
|
if( defined($opt_h) ){
|
|
usage(0);
|
|
}
|
|
|
|
if( defined($opt_v) ){
|
|
print "Version: $version\n";
|
|
exit 0;
|
|
}
|
|
|
|
usage if( !length($opt_r) || (!length($opt_l) && ! length($opt_A))
|
|
|| !length($opt_d) );
|
|
|
|
$opt_f=0 if( ! length($opt_f) );
|
|
|
|
if( ! -d $opt_r ){
|
|
print "Invalid digikam photoroot directory \"$opt_r\". Exit.\n";
|
|
exit 1;
|
|
}
|
|
if( ! -f $opt_d ){
|
|
print "Digikam database \"$opt_d\" not found. Exit.\n";
|
|
exit 1;
|
|
}
|
|
|
|
# Remove trailing /
|
|
$opt_r=~s/\/$//;
|
|
$opt_l=~s/\/$//;
|
|
#
|
|
$rootDir=$opt_r;
|
|
$linkDir=$opt_l;
|
|
|
|
if( length($opt_C) ){
|
|
$cloneMode="full"; # Clone all files/dirs
|
|
}else{
|
|
$cloneMode="minimal"; # Only clone dirs and files haviong tags not all files
|
|
}
|
|
|
|
if( length($opt_H) ){
|
|
$linkMode="hard"; # type of link from linktree to phototree
|
|
}else{
|
|
$linkMode="symbolic"; # type of link from linktree to phototree
|
|
}
|
|
|
|
# Extract data needed from database
|
|
$ret=getTaggedImages(\%images, $opt_d, $opt_r, \%paths, \%pathNames);
|
|
|
|
# Handle Archiving photos and tag structure
|
|
# digikams photo dir will be clone as a whole by using
|
|
# either soft or hard links ($cloneMode)
|
|
if( length($opt_A) ){
|
|
#
|
|
runClone($opt_r, $opt_A, $archiveDirPhotos, $cloneMode, "hard", \%paths, \%pathNames);
|
|
$rootDir="$opt_A/$archiveDirPhotos";
|
|
$linkDir="$opt_A/$archiveDirLinks";
|
|
}
|
|
|
|
if( !$ret && ( length($opt_l) || length($opt_A)) ){
|
|
# When doing hard links we always use absolute linking
|
|
if( $linkMode eq "hard" ){
|
|
warn "Will use absolute hard links for linktree in archive mode...\n" if( !length($opt_a) );
|
|
$opt_a="1";
|
|
}
|
|
|
|
# Create the link trees for all tagged images
|
|
createLinkTree(\%images, $rootDir, $linkDir, $opt_f, $opt_a, $linkMode);
|
|
}
|