1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/openpkg/lsync Tue Jan 06 23:40:39 2009 +0100 1.3 @@ -0,0 +1,339 @@ 1.4 +#!@l_prefix@/lib/openpkg/bash 1.5 +## 1.6 +## lsync -- Access Layer Synchronization Tool 1.7 +## Copyright (c) 2000-2007 OpenPKG Foundation e.V. <http://openpkg.net/> 1.8 +## Copyright (c) 2000-2007 Ralf S. Engelschall <http://engelschall.com/> 1.9 +## 1.10 +## Permission to use, copy, modify, and distribute this software for 1.11 +## any purpose with or without fee is hereby granted, provided that 1.12 +## the above copyright notice and this permission notice appear in all 1.13 +## copies. 1.14 +## 1.15 +## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 1.16 +## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1.17 +## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1.18 +## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR 1.19 +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.20 +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.21 +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 1.22 +## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 1.23 +## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 1.24 +## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 1.25 +## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1.26 +## SUCH DAMAGE. 1.27 +## 1.28 + 1.29 +## 1.30 +## filesystem hierarchy configuration 1.31 +## 1.32 + 1.33 +# program name and version/date 1.34 +progname="lsync" 1.35 +progvers="1.0.4" 1.36 +progdate="04-Aug-2001" 1.37 + 1.38 +# root directory 1.39 +# (if empty, .lsyncrc files provide default) 1.40 +root="" 1.41 + 1.42 +# subdirectory where packages are physically installed 1.43 +pkgdir="PKG" 1.44 + 1.45 +# subdirectories which are synchronized between physically 1.46 +# installed package areas and access layer 1.47 +subdirs="bin,sbin,man,info,include,lib" 1.48 + 1.49 +## 1.50 +## command line option parsing 1.51 +## 1.52 + 1.53 +# default run-time modes 1.54 +nop=0 1.55 +quiet=0 1.56 +trace=0 1.57 +help='' 1.58 +init=0 1.59 +uninstall=0 1.60 +local=0 1.61 + 1.62 +# be aware of .lsyncrc files 1.63 +cwd=`pwd` 1.64 +while [ 1 ]; do 1.65 + if [ -f "$cwd/.lsyncrc" ]; then 1.66 + set -- `cat $cwd/.lsyncrc` "$@" 1.67 + fi 1.68 + [ ".$cwd" = ./ ] && break 1.69 + cwd=`echo $cwd | sed -e 's;/[^/]*$;;' -e 's;^$;/;'` 1.70 +done 1.71 +if [ ".$HOME" != . -a -f "$HOME/.lsyncrc" ]; then 1.72 + set -- `cat $HOME/.lsyncrc` "$@" 1.73 +fi 1.74 + 1.75 +# iterate over argument line 1.76 +for opt 1.77 +do 1.78 + case $opt in 1.79 + -*=*) arg=`echo "$opt" | sed 's/^[-_a-zA-Z0-9]*=//'` ;; 1.80 + *) arg='' ;; 1.81 + esac 1.82 + case $opt in 1.83 + -n|--nop ) nop=1 ;; 1.84 + -q|--quiet ) quiet=1 ;; 1.85 + -t|--trace ) trace=1 ;; 1.86 + -v|--version ) version=1 ;; 1.87 + -h|--help ) help="Usage" ;; 1.88 + -i|--init ) init=1 ;; 1.89 + -u|--uninstall ) uninstall=1 ;; 1.90 + -l|--local ) local=1 ;; 1.91 + --root=* ) root=$arg ;; 1.92 + --pkgdir=* ) pkgdir=$arg ;; 1.93 + --subdirs=* ) subdirs=$arg ;; 1.94 + * ) help="Invalid option \`$opt'"; break ;; 1.95 + esac 1.96 +done 1.97 + 1.98 +# error or usage message 1.99 +if [ ".$help" != . ]; then 1.100 + if [ ".$help" != ".Usage" ]; then 1.101 + echo "$progname:ERROR: $help" 1>&2 1.102 + fi 1.103 + cat 1>&2 <<EOT 1.104 +Usage: $progname [options] 1.105 + 1.106 +Global options: 1.107 + --version, -v display tool version information 1.108 + --help, -h display usage information 1.109 + --init, -i create an initial directory hierarchy 1.110 + 1.111 +Run-time options: 1.112 + --nop, -n perform no filesystem operations 1.113 + --quiet, -q display no verbose messages 1.114 + --trace, -t display performed filesystem operations 1.115 + --local, -l process a local package area only 1.116 + --uninstall, -u uninstall all files 1.117 + 1.118 +Filesystem options: 1.119 + --root=DIR override root directory 1.120 + --pkgdir=DIR override package sub-directory 1.121 + --subdirs=DIR override synchronized sub-directories 1.122 + 1.123 +Current configuration: 1.124 + root directory: $root 1.125 + package root subdir: $pkgdir 1.126 + synchronized subdirs: $subdirs 1.127 +EOT 1.128 + if [ ".$help" != ".Usage" ]; then 1.129 + exit 2 1.130 + else 1.131 + exit 0 1.132 + fi 1.133 +fi 1.134 + 1.135 +# version information 1.136 +if [ ".$version" = .1 ]; then 1.137 + echo "$progname $progvers ($progdate)" 1.138 + exit 0 1.139 +fi 1.140 + 1.141 +# make sure a root directory was found or specified 1.142 +if [ ".$root" = . ]; then 1.143 + echo "$progname:ERROR: no root directory specified!" 1>&2 1.144 + echo "$progname:HINT: use --root=DIR option explicitly on command line" 1>&2 1.145 + echo "$progname:HINT: or implicitly inside an .lsyncrc file in your home" 1>&2 1.146 + echo "$progname:HINT: directory or in any parent directory." 1>&2 1.147 + exit 3 1.148 +fi 1.149 + 1.150 +## 1.151 +## helper functions 1.152 +## 1.153 + 1.154 +display_hd () { 1.155 + if [ ".$headline" != . ]; then 1.156 + if [ ".$quiet" = .0 ]; then 1.157 + echo "$headline" 1.158 + fi 1.159 + headline='' 1.160 + fi 1.161 +} 1.162 + 1.163 +display_op () { 1.164 + if [ ".$quiet" = .0 ]; then 1.165 + echo " $@" 1.166 + fi 1.167 +} 1.168 + 1.169 +display_warning () { 1.170 + echo "$progname:WARNING: $*" 1>&2 1.171 +} 1.172 + 1.173 +display_error () { 1.174 + echo "$progname:ERROR: $*" 1>&2 1.175 +} 1.176 + 1.177 +perform_op () { 1.178 + if [ ".$trace" = .1 ]; then 1.179 + echo " \$ $@" 1.180 + fi 1.181 + if [ ".$nop" = .0 ]; then 1.182 + eval "$@" 1.183 + fi 1.184 +} 1.185 + 1.186 +## 1.187 +## main processing 1.188 +## 1.189 + 1.190 +# extend a "man" subdir to a complete list with subdirs 1.191 +# in order to avoid special cases in the loop processing 1.192 +manex='' 1.193 +if [ ".$init" = .1 ]; then 1.194 + manex='man' 1.195 +fi 1.196 +for i in 1 2 3 4 5 6 7 8; do 1.197 + manex="$manex,man/man$i" 1.198 +done 1.199 +manex=`echo $manex | sed -e 's;^,;;'` 1.200 +subdirs=`echo $subdirs | sed -e "s;man;$manex;"` 1.201 + 1.202 +# special processing: create initial hierarchy 1.203 +if [ ".$init" = .1 ]; then 1.204 + if [ ! -d $root ]; then 1.205 + echo "creating $root" 1.206 + perform_op "mkdir $root" || exit 1 1.207 + fi 1.208 + for subdir in $pkgdir `IFS=,; echo $subdirs`; do 1.209 + if [ ! -d "$root/$subdir" ]; then 1.210 + echo "creating $root/$subdir" 1.211 + perform_op "mkdir $root/$subdir" || exit 1 1.212 + fi 1.213 + done 1.214 + exit 0 1.215 +fi 1.216 + 1.217 +# make sure the root directory actually exists 1.218 +if [ ! -d "$root" ]; then 1.219 + display_warning "root directory \`$root' does not exist" 1.220 + exit 3 1.221 +fi 1.222 + 1.223 +# if processing is restricted to a local package area, pre-determine its name 1.224 +if [ ".$local" = .1 ]; then 1.225 + realroot=`cd $root; pwd` 1.226 + realthis=`pwd` 1.227 + pkgname=`expr "$realthis" : "^$realroot/$pkgdir/\\([^/]*\\).*"` 1.228 + if [ ".$pkgname" = . ]; then 1.229 + display_error "you are not staying under a local package sub-directory" 1.230 + exit 3 1.231 + fi 1.232 +fi 1.233 + 1.234 +# now perform the synchronization for each sub-directory... 1.235 +for subdir in `IFS=,; echo $subdirs`; do 1.236 + headline="$root/$subdir:" 1.237 + 1.238 + # make sure the subdir actually exists in the access layer 1.239 + if [ ! -d "$root/$subdir" ]; then 1.240 + display_warning "access layer directory \`$root/$subdir' does not exist" 1.241 + continue 1.242 + fi 1.243 + 1.244 + # 1.245 + # PASS 1: remove dangling symbolic links in access layer 1.246 + # 1.247 + 1.248 + # iterate over all symlinks in the access layer subdir 1.249 + for link in . `ls "$root/$subdir/" | sed -e "s;^$root/$subdir/*;;g"`; do 1.250 + test ".$link" = ".." && continue 1.251 + 1.252 + # determine the target file of the symlink 1.253 + target=`ls -l "$root/$subdir/$link" 2>/dev/null | sed -e 's;.*-> *;;'` 1.254 + if [ ".$target" = . ]; then 1.255 + display_warning "$root/$subdir/$link seems to be not a symbolic link" 1.256 + continue 1.257 + fi 1.258 + 1.259 + # (optionally) make sure that link target points into local package area 1.260 + if [ ".$local" = .1 -a .`expr $target : "../$pkgdir/$pkgname/.*"` = .0 ]; then 1.261 + continue 1.262 + fi 1.263 + 1.264 + # check whether link is valid, i.e., points to 1.265 + # an existing target file or directory 1.266 + if [ ".$uninstall" = .1 ] ||\ 1.267 + [ ! -f "$root/$subdir/$target" -a \ 1.268 + ! -d "$root/$subdir/$target" ]; then 1.269 + # target no longer exists, so remove dangling symlink 1.270 + display_hd 1.271 + display_op "remove: $link -> $target" 1.272 + perform_op "rm -f $root/$subdir/$link" 1.273 + fi 1.274 + done 1.275 + 1.276 + # if we are uninstalling only, our work is now done 1.277 + if [ ".$uninstall" = ".1" ]; then 1.278 + continue 1.279 + fi 1.280 + 1.281 + # 1.282 + # PASS 2: create new symbolic links in access layer 1.283 + # 1.284 + 1.285 + # calculate the corresponding reverse directory for the current subdir 1.286 + revdir=`echo $subdir | sed -e 's;[^/][^/]*;..;g'` 1.287 + 1.288 + # iterate over all package directories 1.289 + for dir in . `ls "$root/$pkgdir/" | sed -e "s;^$root/$pkgdir/*;;g"`; do 1.290 + test ".$dir" = ".." && continue 1.291 + 1.292 + # (optionally) make sure that we operate only for the local package area 1.293 + if [ ".$local" = .1 -a ".$dir" != ".$pkgname" ]; then 1.294 + continue 1.295 + fi 1.296 + 1.297 + # skip all directories with appended version numbers 1.298 + # in order to support manual versioning of packages 1.299 + case $dir in 1.300 + *-[0-9]* ) continue ;; 1.301 + esac 1.302 + 1.303 + # skip if package directory or package sub-directories has sticky bit set 1.304 + if [ ".`ls -l -d $root/$pkgdir/$dir 2>/dev/null | cut -c10`" = .t ] ||\ 1.305 + [ ".`ls -l -d $root/$pkgdir/$dir/$subdir 2>/dev/null | cut -c10`" = .t ]; then 1.306 + continue 1.307 + fi 1.308 + 1.309 + # check whether the processed subdir exists in package area 1.310 + if [ -d "$root/$pkgdir/$dir/$subdir" ]; then 1.311 + 1.312 + # iterate over all files/directories in package's subdir 1.313 + for file in . `ls "$root/$pkgdir/$dir/$subdir/" |\ 1.314 + sed -e "s;^$root/$pkgdir/$dir/$subdir/*;;g"`; do 1.315 + test ".$file" = ".." && continue 1.316 + 1.317 + # calculate the access layer symlink target 1.318 + target="$revdir/$pkgdir/$dir/$subdir/$file" 1.319 + 1.320 + # check whether a possibly conflicting symlink exists 1.321 + exlink=`ls -l $root/$subdir/$file 2>/dev/null` 1.322 + if [ ".$exlink" != . ]; then 1.323 + extarget=`echo $exlink | sed -e 's;.*-> *;;'` 1.324 + if [ ".$extarget" = . ]; then 1.325 + display_warning "$root/$subdir/$file exits, but seems to be not a symbolic link" 1.326 + elif [ ".$extarget" != ".$target" ]; then 1.327 + display_hd 1.328 + display_op "conflict: $file -> $extarget [existing]" 1.329 + display_op " $file -> $target [alternative]" 1.330 + fi 1.331 + continue 1.332 + fi 1.333 + 1.334 + # create new symlink in access layer 1.335 + display_hd 1.336 + display_op "create: $file -> $target" 1.337 + perform_op "cd $root/$subdir && ln -s $target $file" 1.338 + done 1.339 + fi 1.340 + done 1.341 +done 1.342 +