diff -r 333964c621f1 -r cb59d6afeb61 openpkg/lsync --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openpkg/lsync Tue Jan 06 23:40:39 2009 +0100 @@ -0,0 +1,339 @@ +#!@l_prefix@/lib/openpkg/bash +## +## lsync -- Access Layer Synchronization Tool +## Copyright (c) 2000-2007 OpenPKG Foundation e.V. +## Copyright (c) 2000-2007 Ralf S. Engelschall +## +## 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 <&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 +