summaryrefslogtreecommitdiff
path: root/src/cmd/INIT/ditto.sh
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/INIT/ditto.sh
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/INIT/ditto.sh')
-rw-r--r--src/cmd/INIT/ditto.sh460
1 files changed, 460 insertions, 0 deletions
diff --git a/src/cmd/INIT/ditto.sh b/src/cmd/INIT/ditto.sh
new file mode 100644
index 0000000..1141a0d
--- /dev/null
+++ b/src/cmd/INIT/ditto.sh
@@ -0,0 +1,460 @@
+########################################################################
+# #
+# This software is part of the ast package #
+# Copyright (c) 1994-2011 AT&T Intellectual Property #
+# and is licensed under the #
+# Eclipse Public License, Version 1.0 #
+# by AT&T Intellectual Property #
+# #
+# A copy of the License is available at #
+# http://www.eclipse.org/org/documents/epl-v10.html #
+# (with md5 checksum b35adb5213ca9657e911e9befb180842) #
+# #
+# Information and Software Systems Research #
+# AT&T Research #
+# Florham Park NJ #
+# #
+# Glenn Fowler <gsf@research.att.com> #
+# #
+########################################################################
+: replicate directory hierarchies
+
+COMMAND=ditto
+case `(getopts '[-][123:xyz]' opt --xyz; echo 0$opt) 2>/dev/null` in
+0123) ARGV0="-a $COMMAND"
+ USAGE=$'
+[-?
+@(#)$Id: ditto (AT&T Labs Research) 2010-11-22 $
+]
+'$USAGE_LICENSE$'
+[+NAME?ditto - replicate directory hierarchies]
+[+DESCRIPTION?\bditto\b replicates the \asource\a directory hierarchy
+ to the \adestination\a directory hierarchy. Both \asource\a and
+ \adestination\a may be of the form
+ [\auser\a@]][\ahost\a:]][\adirectory\a]]. At least one of
+ \ahost\a: or \adirectory\a must be specified. The current user is used
+ if \auser@\a is omitted, the local host is used if \ahost\a: is
+ omitted, and the user home directory is used if \adirectory\a is
+ omitted.]
+[+?Remote hosts and files are accessed via \bssh\b(1) or \brsh\b(1). \bksh\b(1),
+ \bpax\b(1), and \btw\b(1) must be installed on the local and remote hosts.]
+[+?For each source file \bditto\b does one of these actions:]{
+ [+chmod|chown?change the mode and/or ownership of the destination
+ file to match the source]
+ [+copy?copy the source file to the destination]
+ [+delete?delete the destination file]
+ [+skip?the destination file is not changed]
+}
+[+?The source and destination hierarchies are generated by \btw\b(1) with
+ the \b--logical\b option. An \b--expr\b option may
+ be specified to prune the search. The \btw\b searches are relative to
+ the \asource\a and \adestination\a directories.]
+[c:checksum?Copy if the \btw\b(1) 32x4 checksum mismatches.]
+[d:delete?Delete \adestination\a files that are not in the \asource\a.]
+[e:expr?\btw\b(1) select expression.]:[tw-expression]
+[m!:mode?Preserve file mode.]
+[n:show?Show the operations but do not exectute.]
+[o:owner?Preserve file user and group ownership.]
+[p:physical?Generate source and destination hierarchies by \btw\b(1) with
+ the \b--physical\b option.]
+[r:remote?The remote access protocol; either \bssh\b or
+ \brsh\b.]:[protocol:=ssh]
+[u:update?Copy only if the \asource\a file is newer than the
+ \adestination\a file.]
+[v:verbose?Trace the operations as they are executed.]
+[D:debug?Enable the debug trace.]
+
+source destination
+
+[+SEE ALSO?\brdist\b(1), \brsync\b(1), \brsh\b(1), \bssh\b(1), \btw\b(1)]
+'
+ ;;
+*) ARGV0=""
+ USAGE="de:[tw-expression]mnouvD source destination"
+ ;;
+esac
+
+usage()
+{
+ OPTIND=0
+ getopts $ARGV0 "$USAGE" OPT '-?'
+ exit 2
+}
+
+parse() # id user@host:dir
+{
+ typeset id dir user host
+ id=$1
+ dir=$2
+ (( debug || ! exec )) && print -r $id $dir
+ if [[ $dir == *@* ]]
+ then
+ user=${dir%%@*}
+ dir=${dir#${user}@}
+ else
+ user=
+ fi
+ if [[ $dir == *:* ]]
+ then
+ host=${dir%%:*}
+ dir=${dir#${host}:}
+ else
+ host=
+ fi
+ if [[ $user ]]
+ then
+ user="-l $user"
+ if [[ ! $host ]]
+ then
+ host=$(hostname)
+ fi
+ fi
+ eval ${id}_user='$user'
+ eval ${id}_host='$host'
+ eval ${id}_dir='$dir'
+}
+
+# initialize
+
+typeset -A chown chmod
+typeset tw cp rm link
+integer ntw=0 ncp=0 nrm=0 nlink=0 n
+
+typeset src_user src_host src_path src_type src_uid src_gid src_perm src_sum
+typeset dst_user dst_host dst_path dst_type dst_uid dst_gid dst_perm dst_sum
+integer src_size src_mtime src_eof
+integer dst_size dst_mtime dst_eof
+
+integer debug=0 delete=0 exec=1 mode=1 owner=0 update=0 verbose=0 logical
+
+typeset remote=ssh trace
+typeset checksum='"-"' pax="pax"
+typeset paxreadflags="" paxwriteflags="--write --format=tgz --nosummary"
+
+tw[ntw++]=tw
+(( logical=ntw ))
+tw[ntw++]=--logical
+tw[ntw++]=--chop
+tw[ntw++]=--ignore-errors
+tw[ntw++]=--expr=sort:name
+
+# grab the options
+
+while getopts $ARGV0 "$USAGE" OPT
+do case $OPT in
+ c) checksum=checksum ;;
+ d) delete=1 ;;
+ e) tw[ntw++]=--expr=\"$OPTARG\" ;;
+ m) mode=0 ;;
+ n) exec=0 verbose=1 ;;
+ o) owner=1 ;;
+ p) tw[logical]=--physical ;;
+ r) remote=$OPTARG ;;
+ u) update=1 ;;
+ v) verbose=1 ;;
+ D) debug=1 ;;
+ *) usage ;;
+ esac
+done
+shift $OPTIND-1
+if (( $# != 2 ))
+then usage
+fi
+tw[ntw++]=--expr=\''action:printf("%d\t%d\t%s\t%s\t%s\t%-.1s\t%o\t%s\t%s\n", size, mtime, '$checksum', uid, gid, mode, perm, path, symlink);'\'
+if (( exec ))
+then
+ paxreadflags="$paxreadflags --read"
+fi
+if (( verbose ))
+then
+ paxreadflags="$paxreadflags --verbose"
+fi
+
+# start the source and destination path list generators
+
+parse src "$1"
+parse dst "$2"
+
+# the |& command may exit before the exec &p
+# the print sync + read delays the |& until the exec &p finishes
+
+if [[ $src_host ]]
+then ($remote $src_user $src_host "{ test ! -f .profile || . ./.profile ;} && cd $src_dir && read && ${tw[*]}") 2>&1 |&
+else (cd $src_dir && read && eval "${tw[@]}") 2>&1 |&
+fi
+exec 5<&p 7>&p
+print -u7 sync
+exec 7>&-
+
+if [[ $dst_host ]]
+then ($remote $dst_user $dst_host "{ test ! -f .profile || . ./.profile ;} && cd $dst_dir && read && ${tw[*]}") 2>&1 |&
+else (cd $dst_dir && read && eval "${tw[@]}") 2>&1 |&
+fi
+exec 6<&p 7>&p
+print -u7 sync
+exec 7>&-
+
+# scan through the sorted path lists
+
+if (( exec ))
+then
+ src_skip=*
+ dst_skip=*
+else
+ src_skip=
+ dst_skip=
+fi
+src_path='' src_eof=0
+dst_path='' dst_eof=0
+ifs=${IFS-$' \t\n'}
+IFS=$'\t'
+while :
+do
+ # get the next source path
+
+ if [[ ! $src_path ]] && (( ! src_eof ))
+ then
+ if read -r -u5 text src_mtime src_sum src_uid src_gid src_type src_perm src_path src_link
+ then
+ if [[ $text != +([[:digit:]]) ]]
+ then
+ print -u2 $COMMAND: source: "'$text'"
+ src_path=
+ continue
+ fi
+ src_size=$text
+ elif (( dst_eof ))
+ then
+ break
+ elif (( src_size==0 ))
+ then
+ exit 1
+ else
+ src_path=
+ src_eof=1
+ fi
+ fi
+
+ # get the next destination path
+
+ if [[ ! $dst_path ]] && (( ! dst_eof ))
+ then
+ if read -r -u6 text dst_mtime dst_sum dst_uid dst_gid dst_type dst_perm dst_path dst_link
+ then
+ if [[ $text != +([[:digit:]]) ]]
+ then
+ print -u2 $COMMAND: destination: $text
+ dst_path=
+ continue
+ fi
+ dst_size=$text
+ elif (( src_eof ))
+ then
+ break
+ elif (( dst_size==0 ))
+ then
+ exit 1
+ else
+ dst_path=
+ dst_eof=1
+ fi
+ fi
+
+ # determine the { cp rm chmod chown } ops
+
+ if (( debug ))
+ then
+ [[ $src_path ]] && print -r -u2 -f $': src %8s %10s %s %s %s %s %3s %s\n' $src_size $src_mtime $src_sum $src_uid $src_gid $src_type $src_perm "$src_path"
+ [[ $dst_path ]] && print -r -u2 -f $': dst %8s %10s %s %s %s %s %3s %s\n' $dst_size $dst_mtime $dst_sum $dst_uid $dst_gid $dst_type $dst_perm "$dst_path"
+ fi
+ if [[ $src_path == $dst_path ]]
+ then
+ if [[ $src_type != $dst_type ]]
+ then
+ rm[nrm++]=$dst_path
+ if [[ $dst_path != $dst_skip ]]
+ then
+ if [[ $dst_type == d ]]
+ then
+ dst_skip="$dst_path/*"
+ print -r rm -r "'$dst_path'"
+ else
+ dst_skip=
+ print -r rm "'$dst_path'"
+ fi
+ fi
+ fi
+ if [[ $src_type == l ]]
+ then if [[ $src_link != $dst_link ]]
+ then
+ cp[ncp++]=$src_path
+ if [[ $src_path != $src_skip ]]
+ then
+ src_skip=
+ print -r cp "'$src_path'"
+ fi
+ fi
+ elif [[ $src_type != d ]] && { (( update && src_mtime > dst_mtime )) || (( ! update )) && { (( src_size != dst_size )) || [[ $src_sum != $dst_sum ]] ;} ;}
+ then
+ if [[ $src_path != . ]]
+ then
+ cp[ncp++]=$src_path
+ if [[ $src_path != $src_skip ]]
+ then
+ src_skip=
+ print -r cp "'$src_path'"
+ fi
+ fi
+ else
+ if (( owner )) && [[ $src_uid != $dst_uid || $src_gid != $dst_gid ]]
+ then
+ chown[$src_uid.$src_gid]="${chown[$src_uid.$src_gid]} '$src_path'"
+ if [[ $src_path != $src_skip ]]
+ then
+ src_skip=
+ print -r chown $src_uid.$src_gid "'$src_path'"
+ fi
+ if (( (src_perm & 07000) || mode && src_perm != dst_perm ))
+ then
+ chmod[$src_perm]="${chmod[$src_perm]} '$src_path'"
+ if [[ $src_path != $src_skip ]]
+ then
+ src_skip=
+ print -r chmod $src_perm "'$src_path'"
+ fi
+ fi
+ elif (( mode && src_perm != dst_perm ))
+ then
+ chmod[$src_perm]="${chmod[$src_perm]} '$src_path'"
+ if [[ $src_path != $src_skip ]]
+ then
+ src_skip=
+ print -r chmod $src_perm "'$src_path'"
+ fi
+ fi
+ fi
+ src_path=
+ dst_path=
+ elif [[ ! $dst_path || $src_path && $src_path < $dst_path ]]
+ then
+ if [[ $src_path != . ]]
+ then
+ cp[ncp++]=$src_path
+ if [[ $src_path != $src_skip ]]
+ then
+ if [[ $src_type == d ]]
+ then
+ src_skip="$src_path/*"
+ print -r cp -r "'$src_path'"
+ else
+ src_skip=
+ print -r cp "'$src_path'"
+ fi
+ fi
+ fi
+ src_path=
+ elif [[ $dst_path ]]
+ then
+ if (( delete ))
+ then
+ rm[nrm++]=$dst_path
+ if [[ $dst_path != $dst_skip ]]
+ then
+ if [[ $dst_type == d ]]
+ then
+ dst_skip="$dst_path/*"
+ print -r rm -r "'$dst_path'"
+ else
+ dst_skip=
+ print -r rm "'$dst_path'"
+ fi
+ fi
+ fi
+ dst_path=
+ fi
+done
+IFS=$ifs
+
+(( exec )) || exit 0
+
+# generate, transfer and execute the { rm chown chmod } script
+
+if (( ${#rm[@]} || ${#chmod[@]} || ${#chown[@]} ))
+then
+ {
+ if (( verbose ))
+ then
+ print -r -- set -x
+ fi
+ print -nr -- cd "'$dst_dir'"
+ n=0
+ for i in ${rm[@]}
+ do
+ if (( --n <= 0 ))
+ then
+ n=32
+ print
+ print -nr -- rm -rf
+ fi
+ print -nr -- " '$i'"
+ done
+ for i in ${!chown[@]}
+ do
+ n=0
+ for j in ${chown[$i]}
+ do
+ if (( --n <= 0 ))
+ then
+ n=32
+ print
+ print -nr -- chown $i
+ fi
+ print -nr -- " $j"
+ done
+ done
+ for i in ${!chmod[@]}
+ do
+ n=0
+ for j in ${chmod[$i]}
+ do
+ if (( --n <= 0 ))
+ then
+ n=32
+ print
+ print -nr -- chmod $i
+ fi
+ print -nr -- " $j"
+ done
+ done
+ print
+ } | {
+ if (( ! exec ))
+ then
+ cat
+ elif [[ $dst_host ]]
+ then
+ $remote $dst_user $dst_host sh
+ else
+ $SHELL
+ fi
+ }
+fi
+
+# generate, transfer and read back the { cp } tarball
+
+if (( ${#cp[@]} ))
+then
+ {
+ cd $src_dir &&
+ print -r -f $'%s\n' "${cp[@]}" |
+ $pax $paxwriteflags
+ } | {
+ if [[ $dst_host ]]
+ then
+ $remote $dst_user $dst_host "{ test ! -f .profile || . ./.profile ;} && { test -d \"$dst_dir\" || mkdir -p \"$dst_dir\" ;} && cd \"$dst_dir\" && gunzip | $pax $paxreadflags"
+ else
+ ( { test -d "$dst_dir" || mkdir -p "$dst_dir" ;} && cd "$dst_dir" && gunzip | $pax $paxreadflags )
+ fi
+ }
+ wait
+fi