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.
 
 
 
 
 
 

338 lines
10 KiB

#!/bin/sh
##
## lsync -- Access Layer Synchronization Tool
## Copyright (c) 2000-2002 Ralf S. Engelschall <rse@engelschall.com>
##
## Permission to use, copy, modify, and distribute this software for
## any purpose with or without fee is hereby granted, provided that
## the above copyright notice and this permission notice appear in all
## copies.
##
## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
## SUCH DAMAGE.
##
##
## filesystem hierarchy configuration
##
# program name and version/date
progname="lsync"
progvers="1.0.4"
progdate="04-Aug-2001"
# root directory
# (if empty, .lsyncrc files provide default)
root=""
# subdirectory where packages are physically installed
pkgdir="PKG"
# subdirectories which are synchronized between physically
# installed package areas and access layer
subdirs="bin,sbin,man,info,include,lib"
##
## command line option parsing
##
# default run-time modes
nop=0
quiet=0
trace=0
help=''
init=0
uninstall=0
local=0
# be aware of .lsyncrc files
cwd=`pwd`
while [ 1 ]; do
if [ -f "$cwd/.lsyncrc" ]; then
set -- `cat $cwd/.lsyncrc` "$@"
fi
[ ".$cwd" = ./ ] && break
cwd=`echo $cwd | sed -e 's;/[^/]*$;;' -e 's;^$;/;'`
done
if [ ".$HOME" != . -a -f "$HOME/.lsyncrc" ]; then
set -- `cat $HOME/.lsyncrc` "$@"
fi
# iterate over argument line
for opt
do
case $opt in
-*=*) arg=`echo "$opt" | sed 's/^[-_a-zA-Z0-9]*=//'` ;;
*) arg='' ;;
esac
case $opt in
-n|--nop ) nop=1 ;;
-q|--quiet ) quiet=1 ;;
-t|--trace ) trace=1 ;;
-v|--version ) version=1 ;;
-h|--help ) help="Usage" ;;
-i|--init ) init=1 ;;
-u|--uninstall ) uninstall=1 ;;
-l|--local ) local=1 ;;
--root=* ) root=$arg ;;
--pkgdir=* ) pkgdir=$arg ;;
--subdirs=* ) subdirs=$arg ;;
* ) help="Invalid option \`$opt'"; break ;;
esac
done
# error or usage message
if [ ".$help" != . ]; then
if [ ".$help" != ".Usage" ]; then
echo "$progname:ERROR: $help" 1>&2
fi
cat 1>&2 <<EOT
Usage: $progname [options]
Global options:
--version, -v display tool version information
--help, -h display usage information
--init, -i create an initial directory hierarchy
Run-time options:
--nop, -n perform no filesystem operations
--quiet, -q display no verbose messages
--trace, -t display performed filesystem operations
--local, -l process a local package area only
--uninstall, -u uninstall all files
Filesystem options:
--root=DIR override root directory
--pkgdir=DIR override package sub-directory
--subdirs=DIR override synchronized sub-directories
Current configuration:
root directory: $root
package root subdir: $pkgdir
synchronized subdirs: $subdirs
EOT
if [ ".$help" != ".Usage" ]; then
exit 2
else
exit 0
fi
fi
# version information
if [ ".$version" = .1 ]; then
echo "$progname $progvers ($progdate)"
exit 0
fi
# make sure a root directory was found or specified
if [ ".$root" = . ]; then
echo "$progname:ERROR: no root directory specified!" 1>&2
echo "$progname:HINT: use --root=DIR option explicitly on command line" 1>&2
echo "$progname:HINT: or implicitly inside an .lsyncrc file in your home" 1>&2
echo "$progname:HINT: directory or in any parent directory." 1>&2
exit 3
fi
##
## helper functions
##
display_hd () {
if [ ".$headline" != . ]; then
if [ ".$quiet" = .0 ]; then
echo "$headline"
fi
headline=''
fi
}
display_op () {
if [ ".$quiet" = .0 ]; then
echo " $@"
fi
}
display_warning () {
echo "$progname:WARNING: $*" 1>&2
}
display_error () {
echo "$progname:ERROR: $*" 1>&2
}
perform_op () {
if [ ".$trace" = .1 ]; then
echo " \$ $@"
fi
if [ ".$nop" = .0 ]; then
eval "$@"
fi
}
##
## main processing
##
# extend a "man" subdir to a complete list with subdirs
# in order to avoid special cases in the loop processing
manex=''
if [ ".$init" = .1 ]; then
manex='man'
fi
for i in 1 2 3 4 5 6 7 8; do
manex="$manex,man/man$i"
done
manex=`echo $manex | sed -e 's;^,;;'`
subdirs=`echo $subdirs | sed -e "s;man;$manex;"`
# special processing: create initial hierarchy
if [ ".$init" = .1 ]; then
if [ ! -d $root ]; then
echo "creating $root"
perform_op "mkdir $root" || exit 1
fi
for subdir in $pkgdir `IFS=,; echo $subdirs`; do
if [ ! -d "$root/$subdir" ]; then
echo "creating $root/$subdir"
perform_op "mkdir $root/$subdir" || exit 1
fi
done
exit 0
fi
# make sure the root directory actually exists
if [ ! -d "$root" ]; then
display_warning "root directory \`$root' does not exist"
exit 3
fi
# if processing is restricted to a local package area, pre-determine its name
if [ ".$local" = .1 ]; then
realroot=`cd $root; pwd`
realthis=`pwd`
pkgname=`expr "$realthis" : "^$realroot/$pkgdir/\\([^/]*\\).*"`
if [ ".$pkgname" = . ]; then
display_error "you are not staying under a local package sub-directory"
exit 3
fi
fi
# now perform the synchronization for each sub-directory...
for subdir in `IFS=,; echo $subdirs`; do
headline="$root/$subdir:"
# make sure the subdir actually exists in the access layer
if [ ! -d "$root/$subdir" ]; then
display_warning "access layer directory \`$root/$subdir' does not exist"
continue
fi
#
# PASS 1: remove dangling symbolic links in access layer
#
# iterate over all symlinks in the access layer subdir
for link in . `ls "$root/$subdir/" | sed -e "s;^$root/$subdir/*;;g"`; do
test ".$link" = ".." && continue
# determine the target file of the symlink
target=`ls -l "$root/$subdir/$link" 2>/dev/null | sed -e 's;.*-> *;;'`
if [ ".$target" = . ]; then
display_warning "$root/$subdir/$link seems to be not a symbolic link"
continue
fi
# (optionally) make sure that link target points into local package area
if [ ".$local" = .1 -a .`expr $target : "../$pkgdir/$pkgname/.*"` = .0 ]; then
continue
fi
# check whether link is valid, i.e., points to
# an existing target file or directory
if [ ".$uninstall" = .1 ] ||\
[ ! -f "$root/$subdir/$target" -a \
! -d "$root/$subdir/$target" ]; then
# target no longer exists, so remove dangling symlink
display_hd
display_op "remove: $link -> $target"
perform_op "rm -f $root/$subdir/$link"
fi
done
# if we are uninstalling only, our work is now done
if [ ".$uninstall" = ".1" ]; then
continue
fi
#
# PASS 2: create new symbolic links in access layer
#
# calculate the corresponding reverse directory for the current subdir
revdir=`echo $subdir | sed -e 's;[^/][^/]*;..;g'`
# iterate over all package directories
for dir in . `ls "$root/$pkgdir/" | sed -e "s;^$root/$pkgdir/*;;g"`; do
test ".$dir" = ".." && continue
# (optionally) make sure that we operate only for the local package area
if [ ".$local" = .1 -a ".$dir" != ".$pkgname" ]; then
continue
fi
# skip all directories with appended version numbers
# in order to support manual versioning of packages
case $dir in
*-[0-9]* ) continue ;;
esac
# skip if package directory or package sub-directories has sticky bit set
if [ ".`ls -l -d $root/$pkgdir/$dir 2>/dev/null | cut -c10`" = .t ] ||\
[ ".`ls -l -d $root/$pkgdir/$dir/$subdir 2>/dev/null | cut -c10`" = .t ]; then
continue
fi
# check whether the processed subdir exists in package area
if [ -d "$root/$pkgdir/$dir/$subdir" ]; then
# iterate over all files/directories in package's subdir
for file in . `ls "$root/$pkgdir/$dir/$subdir/" |\
sed -e "s;^$root/$pkgdir/$dir/$subdir/*;;g"`; do
test ".$file" = ".." && continue
# calculate the access layer symlink target
target="$revdir/$pkgdir/$dir/$subdir/$file"
# check whether a possibly conflicting symlink exists
exlink=`ls -l $root/$subdir/$file 2>/dev/null`
if [ ".$exlink" != . ]; then
extarget=`echo $exlink | sed -e 's;.*-> *;;'`
if [ ".$extarget" = . ]; then
display_warning "$root/$subdir/$file exits, but seems to be not a symbolic link"
elif [ ".$extarget" != ".$target" ]; then
display_hd
display_op "conflict: $file -> $extarget [existing]"
display_op " $file -> $target [alternative]"
fi
continue
fi
# create new symlink in access layer
display_hd
display_op "create: $file -> $target"
perform_op "cd $root/$subdir && ln -s $target $file"
done
fi
done
done