#! /bin/bash
#
# adjust links, e.g. after renaming a directory
#
# adjln requires three arguments
#  root of directory tree to search for links
#  old target directory tree root
#  new target directory tree root
#
# adjln takes the following options
#  -b    break on first incorrect link found
#  -d    print debug output
#  -f    force re-link even if there is a problem
#  -s    simulate, don't actually re-link
#  -v    verbose
#
# adjln changes all links in the root directory tree
# that point to files in the old directory tree
# to point to files in the new directory tree
#
# Version 0.1 - initial release
#
# Malte Forkel <malte.forkel@berlin.de>

USAGE="Usage: $(basename $0) [-bdfsv] [-g group] [-u user] rootdir olddir newdir"
NUMARGS=3

BREAK=false
DEBUG=false
FORCE=false
SIMULATE=false
VERBOSE=false

GROUP=""
USER=""

NOERR=0
ARGERR=64
OPTERR=65
LNKERR=71
LNKACT=0

# parse options
while getopts "dfsv" OPT; do
   case $OPT in
      b )
         BREAK=true;;
      d )
         DEBUG=true;;
      f )
         FORCE=true;;
      s )
         SIMULATE=true;;
      v )
         VERBOSE=true;;
      [\?*] )
         echo $USAGE
         exit $OPTERR;;
   esac
done
shift $(($OPTIND - 1))

# check number of arguments
if [ $# -ne $NUMARGS ]; then
   echo $USAGE
   exit $ARGERR
fi

# assign arguments, strip trailing /
rootdir=${1%/}
olddir=${2%/}
newdir=${3%/}

# force absolute path names
_PWD=${PWD%/}
if [ ! -z ${olddir%%/*} ]; then
   olddir="$_PWD/$olddir"
fi
if [ ! -z ${newdir%%/*} ]; then
   newdir="$_PWD/$newdir"
fi

# check arguments
if ! $SIMULATE && [ ! -d "$newdir" ]; then
   echo "$(basename $0): \"$newdir\" is not a directory"
   exit $ARGERR
elif [ $"olddir" = "$newdir" ]; then
   echo "$(basename $0): newdir must be different from olddir"
   exit $ARGERR
fi

# find all links to the old directory
if $DEBUG; then
   echo "# searching links in $rootdir ..."
fi
for lnk in $(find "$rootdir" -lname "$olddir/*"); do
   if $DEBUG; then
      echo "# link: $lnk"
   fi
   # determine old target and new target
   oldtgt=$(readlink "$lnk")
   newtgt="$newdir${oldtgt##$olddir}"
   if $DEBUG; then
      echo "# old target: $oldtgt"
      echo "# new target: $newtgt"
   fi
   owngrp=$(stat "$lnk" --printf %U:%G)
   # test status of old target and new target
   #if $FORCE || [ -L $lnk -a ! -f $oldtgt -a -f $newtgt ]; then
   if $FORCE || [ -L $lnk ]; then
      # re-link to new target
      #lncmd="ln -fs \"$newtgt\" \"$lnk\""
      lncmd="ln -fs $newtgt $lnk"
      ogcmd="chown -h $owngrp $lnk"
      if $SIMULATE; then
         echo $lncmd
         echo $ogcmd
      else
         if $VERBOSE; then
            echo "re-linking $lnk"
         fi
         $lncmd
         $ogcmd
      fi
   else
      # report problem
      if ! [ -L $lnk ] ; then echo "not a symbolic link: $lnk" ; fi
      if [ -f $oldtgt ] ; then echo "old target exists: $oldtgt" ; fi
      if ! [ -f $newtgt ] ; then echo "new target missing: $newtgt" ; fi
      if $BREAK; then
         exit $LNKERR
      fi
   fi
done

exit $NOERR