open_toontown_panda3d/dtool/src/attach/neartool

1057 lines
24 KiB
Bash
Executable File

#! /usr/local/bin/bash
#
# neartool - a Clearcase emulator for working off-line.
#
# Usage:
#
# neartool mkelem [-c "comment"] [-nc] [-eltype type] filename
# Marks a new file as a versioned element and checks it out.
#
# neartool mkdir [-c "comment"] [-nc] new_dir
# Creates a new Clearcase directory.
#
# neartool mv old-file new-file
# Renames or moves a versioned element.
#
# neartool mv file1 file2 file3 ... to-dir
# Moves a number of versioned elements into a new directory.
#
# neartool (co|checkout) [-c "comment"] [-nc] filename
# "checks out" a file by saving its current version and making a new
# version writable. The file is also marked as "checked-out".
#
# neartool (unco|uncheckout) filename
# Reverses the effect of the previous checkout.
#
# neartool (ci|checkin) [-c "comment"] [-nc] filename
# Marks the file as "checked in" and finalizes its most recent
# version, making it read-only.
#
# neartool revert filename
# Backs up a version on a checked-in filename.
#
# neartool revertall filename
# Reverts a filename all the way to its original state as extracted
# from the Clearcase vobs.
#
# neartool collapse filename
# Collapse all versioning information and mark the file as
# "checked-in" and merged as is. Presumably this should only be
# done after merging the changes back into the actual clearcase
# vobs.
#
# neartool rmname filename
# Removes a filename from a Clearcase directory.
#
# neartool find root [opts]
# Finds all elements with versions at root or below. The options
# are ignored. This is just a hack to support ctihave.
#
# neartool (lsco|lscheckout) [-l] file1 file2 file3...
# Lists which of the files are currently checked out, if any. If no
# filenames are given, lists all of the checked out files in the
# current directory. The -l option is ignored.
#
# neartool diff [-pre] filename
# Performs a diff between the indicated filename and its previous
# version. The option -pre is ignored.
#
# neartool xdiff [-pre] filename
# Performs an xdiff between the indicated filename and its previous
# version. The option -pre is ignored.
#
#ENDCOMMENT
#
# get_nth_param ( param-no param1 param2 param3 ... )
#
# Sets the variable nth_param to the value of the parameter
# corresponding to param-no.
#
function get_nth_param {
local param_no=$1
shift $param_no
nth_param=$1
}
#
# get_last_param ( param1 param2 param3 ... )
#
# Sets the variable last_param to the value of the last non-blank
# parameter.
#
function get_last_param {
last_param="$1"
shift
while [ ! -z "$*" ]; do
last_param="$1"
shift
done
}
#
# format_comment ( comment_str )
#
# Writes the parameter -c '$comment_str', or -nc if the comment is
# empty, to the standard output. Handles nested quotes appropriately.
#
function format_comment {
local comment="$*"
if [ -z "$comment" ]; then
echo "-nc"
else
# Escaping single quotes inside a single-quoted string doesn't
# seem to work too reliably. Instead, we'll just remove single
# quotes. So there.
# This absurd number of backslashes is needed to output a single
# backslash through all this nesting.
# echo "-c '"`echo $comment | sed "s/'/\\\\\\\\'/g"`"'"
echo "-c '"`echo $comment | sed "s/'//g"`"'"
fi
}
#
# list_comments ( dirname basename )
#
# Writes to stdout any comments associated with checked-out versions of
# the indicated file, in order.
#
function list_comments {
local dirname=$1
local basename=$2
local filename=$dirname/$basename
local file comment version
if [ -f $dirname/.ct0.$basename ]; then
# Now look for comments, in version-number order.
# We use a series of ls commands so we don't try to sort the
# filenames between the one-, two-, and three-digit version
# numbers.
for file in `(cd $dirname; ls .ct[0-9].$basename; ls .ct[0-9][0-9].$basename; ls .ct[0-9][0-9][0-9].$basename) 2>/dev/null`; do
version=`echo $file | sed "s/^\.ct\([0-9]*\).*$/\1/"`
comment=$dirname/.ct${version}comment.$basename
if [ -f $comment ]; then
sed s'/^/ /' <$comment
fi
done
fi
}
#
# get_highest_version ( dirname basename )
#
# Sets the variable $version to the numeric value that is the highest
# existing version number defined for the given filename. If the
# filename has no previous version numbers, sets $version to -1.
#
function get_highest_version {
local dirname=$1
local basename=$2
local filename=$dirname/$basename
local last
if [ -f $dirname/.ct0.$basename ]; then
# If there are any versions at all, get the highest-numbered one
# and return it.
# We use a series of ls commands so we don't try to sort the
# filenames between the one-, two-, and three-digit version
# numbers.
last=`(cd $dirname; ls .ct[0-9].$basename; ls .ct[0-9][0-9].$basename; ls .ct[0-9][0-9][0-9].$basename) 2>/dev/null | tail -1`
version=`echo $last | sed "s/^\.ct\([0-9]*\).*$/\1/"`
else
# If there aren't any versions yet, the highest-numbered existing
# version number is -1.
version=-1
fi
}
#
# is_ctinternal ( basename )
#
# Returns success if the filename matches one of the internal filenames
# generated by this script, failure if it is a perfectly ordinary
# non-ct file.
#
function is_ctinternal {
local basename=$1
case $basename in
.ct[0-9].*|ct[0-9][0-9].*|ct[0-9][0-9][0-9].*|\
.ct[0-9]comment.*|.ct[0-9][0-9]comment.*|.ct[0-9][0-9][0-9]comment*|\
.ctco.*|.ctnew.*)
return 0;;
esac
return 1
}
#
# is_install_dir ( dirname )
#
# Returns success if the directory is any of the directories known to
# be install directories, such as inc, lib, lib/ss, etc.
#
function is_install_dir {
local dirname=$1
get_rel_dir $projroot $dirname
case $rel_dir in
inc) return 0;;
inc/*) return 0;;
include/*) return 0;;
lib) return 0;;
lib/*) return 0;;
esac
return 1
}
#
# rm_all_ctinternals ( dirname basename )
#
# Removes all the ctinternal files associated with the indicated file.
#
function rm_all_ctinternals {
local dirname=$1
local basename=$2
rm -f $dirname/.ct[0-9].$basename
rm -f $dirname/.ct[0-9][0-9].$basename
rm -f $dirname/.ct[0-9][0-9][0-9].$basename
rm -f $dirname/.ct[0-9]comment.$basename
rm -f $dirname/.ct[0-9][0-9]comment.$basename
rm -f $dirname/.ct[0-9][0-9][0-9]comment.$basename
rm -f $dirname/.ctnew.$basename
rm -f $dirname/.ctco.$basename
}
#
# sane_filename ( dirname basename )
#
# Performs some sanity checks on the filename. If the file is
# unusable, exits the script with a status of 1.
#
function sane_filename {
local dirname=$1
local basename=$2
local filename=$dirname/$basename
if [ -z "$basename" ]; then
echo "Filename unspecified." 1>&2
exit 1
fi
if is_ctinternal $basename; then
echo "$filename is an internal filename and should not be checked out." 1>&2
exit 1
fi
# It's easy to accidentally attempt to check something out from the
# inc directory. Let's detect this.
if is_install_dir $dirname; then
echo "$basename is in an install directory." 1>&2
echo "It should probably not be checked out." 1>&2
exit 1
fi
if [ -d $filename ]; then
# Let's just quietly ignore directories. If we issue the warning
# statement, it makes ctaddtgt and ctaddpkg seem like they're
# failing.
# echo "Cannot operate on directories." 1>&2
exit 0
fi
}
#
# get_fullpath ( local_dir )
#
# Sets $fullpath to the fully-qualified pathname associated with $local_dir.
#
function get_fullpath {
local local_dir=$1
if [ -z "$local_dir" ]; then
fullpath=`pwd`
else
if [ ! -d "$local_dir" ]; then
echo "Invalid directory: $local_dir" 1>&2
exit 1
fi
# If we use pwd instead of /bin/pwd, $PWD will be used, which will give
# the wrong answer
fullpath=`(cd $local_dir; /bin/pwd)`
fi
}
#
# get_rel_dir ( root_dir local_dir )
#
# Sets $rel_dir to the string which represents $local_dir relative to
# $root_dir. This is a simple string-prefix operation, and could fail
# in some obscure cases.
#
function get_rel_dir {
get_fullpath $1
local root_dir=$fullpath
get_fullpath $2
local local_dir=$fullpath
# Now remove the initial prefix.
if [ "$root_dir" = "$local_dir" ]; then
rel_dir="."
else
rel_dir=`echo $local_dir | sed 's:^'$root_dir/'::'`
if [ "$rel_dir" = "$local_dir" ]; then
echo "$local_dir is not a directory within $root_dir." 1>&2
exit 1
fi
fi
}
function ctmkelem {
local filename dirname basename
local comment eltype
eltype=text_file
while getopts c:n:e: flag; do
case $flag in
c) comment="$OPTARG";;
n) comment="";;
e) case $OPTARG in
ltype) get_nth_param $OPTIND "$@"
eltype=$nth_param
OPTIND=`expr $OPTIND + 1`;;
*) echo Invalid switch -e$OPTARG
exit 1;
esac;;
\?) exit 1;
esac
done
shift `expr $OPTIND - 1`
if [ -z "$1" ]; then
echo "No filenames given."
exit 1
fi
for filename in "$@"; do
dirname=`dirname $filename`
basename=`basename $filename`
sane_filename $dirname $basename
ctco=$dirname/.ctco.$basename
if [ -f $ctco ]; then
echo "$filename is already checked out." 1>&2
exit 1
fi
if [ ! -f $filename ]; then
# If the file doesn't exist, create it.
echo -n "" >$filename
fi
get_highest_version $dirname $basename
if [ $version -ne -1 ]; then
echo "$filename already has versions." 1>&2
exit 1
fi
if [ ! -w $filename ]; then
echo "$filename has no write permission, it's probably already a versioned element." 1>&2
exit 1
fi
# Create a ctnew file to indicate the file is newly created.
ctnew=$dirname/.ctnew.$basename
echo $eltype >$ctnew
chmod uga-w $ctnew
# Now "check out" the new filename by creating an empty first version.
next=$dirname/.ct0.$basename
nextcomment=$dirname/.ct0comment.$basename
if [ ! -z "$comment" ]; then
echo $comment >$nextcomment
chmod uga-w $nextcomment
fi
echo -n "" > $next
date >$ctco
done
}
function ctmkdir {
local filename dirname basename
local comment
while getopts c:n: flag; do
case $flag in
c) comment="$OPTARG";;
n) comment="";;
\?) exit 1;
esac
done
shift `expr $OPTIND - 1`
if [ -z "$1" ]; then
echo "No filenames given."
exit 1
fi
for filename in "$@"; do
dirname=`dirname $filename`
basename=`basename $filename`
if [ -z "$basename" ]; then
echo "Filename unspecified." 1>&2
exit 1
fi
if is_ctinternal $basename; then
echo "$filename is an internal filename and should not be checked out." 1>&2
exit 1
fi
if mkdir $filename; then
# The directory is successfully made; record this in the
# Clearcase instructions.
get_rel_dir $projroot $dirname
echo "ctco -nc $rel_dir" >>$projroot/.ctcmds
echo "ctmkdir `format_comment $comment` $rel_dir/$basename" >>$projroot/.ctcmds
else
echo "Unable to create directory $filename." 1>&2
fi
done
}
#
# do_ctmv ( oldname newname )
#
# The implementation of ctmv. This renames a single file, possibly
# placing it in a new directory.
#
function do_ctmv {
local oldfilename olddirname oldbasename
local newfilename newdirname newbasename
oldfilename=$1
olddirname=`dirname $oldfilename`
oldbasename=`basename $oldfilename`
newfilename=$2
newdirname=`dirname $newfilename`
newbasename=`basename $newfilename`
sane_filename $olddirname $oldbasename
sane_filename $newdirname $newbasename
if [ ! -f $oldfilename ]; then
echo "$oldfilename does not exist." 1>&2
exit 1
fi
if [ -f $newfilename ]; then
echo "$newfilename already exists--remove it first." 1>&2
exit 1
fi
oldctnew=$olddirname/.ctnew.$oldfilename
if [ ! -f $oldctnew ]; then
# The filename exists on the actual Clearcase vobs. We need to
# record the renaming.
get_rel_dir $projroot $olddirname
local oldroot=$rel_dir
get_rel_dir $projroot $newdirname
local newroot=$rel_dir
echo "ctco -nc $oldroot" >> $projroot/.ctcmds
if [ "$oldroot" != "$newroot" ]; then
echo "ctco -nc $newroot" >> $projroot/.ctcmds
fi
echo "ctmv $oldroot/$oldbasename $newroot/$newbasename" >> $projroot/.ctcmds
fi
# Now rename our local copy, and all of its version tracking stuff.
get_fullpath $newdirname
local newroot=$fullpath
(cd $olddirname;
for file in \
`ls .ct[0-9].$oldbasename \
.ct[0-9][0-9].$oldbasename \
.ct[0-9][0-9][0-9].$oldbasename \
.ct[0-9]comment.$oldbasename \
.ct[0-9][0-9]comment.$oldbasename \
.ct[0-9][0-9][0-9]comment.$oldbasename \
.ctnew.$oldbasename \
.ctco.$oldbasename \
$oldbasename 2>/dev/null`; do
ctprefix=`echo $file | sed 's:'$oldbasename'$::'`
mv -i $file $newroot/$ctprefix$newbasename
done)
}
function ctmv {
local source destination
while getopts "" flag; do
case $flag in
\?) exit 1;
esac
done
shift `expr $OPTIND - 1`
get_last_param "$@"
destination=$last_param;
if [ ! -d "$destination" ]; then
# If we're not moving to a directory, we are renaming a file--and
# thus we can only allow two parameters.
source=$1
shift 2
if [ ! -z "$*" ]; then
echo Last filename must be a directory.
exit 1
fi
do_ctmv $source $destination
else
# Otherwise, we're moving a slew of files to a directory. Get
# each filename and move it.
for source in "$@"; do
if [ "$source" != "$destination" ]; then
do_ctmv $source $destination/$source
fi
done
fi
}
function ctco {
local filename dirname basename
while getopts c:n: flag; do
case $flag in
c) comment="$OPTARG";;
n) comment="";;
\?) exit 1;
esac
done
shift `expr $OPTIND - 1`
if [ -z "$1" ]; then
echo "No filenames given."
exit 1
fi
for filename in "$@"; do
dirname=`dirname $filename`
basename=`basename $filename`
sane_filename $dirname $basename
okflag=y
if [ ! -f $filename ]; then
echo "$filename does not exist." 1>&2
okflag=
else
ctco=$dirname/.ctco.$basename
if [ -f $ctco ]; then
echo "$filename is already checked out." 1>&2
okflag=
else
if [ -w $filename ]; then
echo "$filename has write permission, it's probably not a versioned element." 1>&2
echo "Try neartool mkelem $filename." 1>&2
okflag=
fi
fi
fi
if [ ! -z "$okflag" ]; then
get_highest_version $dirname $basename
next=$dirname/.ct`expr $version + 1`.$basename
nextcomment=$dirname/.ct`expr $version + 1`comment.$basename
if [ ! -z "$comment" ]; then
echo $comment >$nextcomment
chmod uga-w $nextcomment
fi
mv $filename $next
cp $next $filename
chmod ug+w $filename
date >$ctco
fi
done
}
function ctunco {
local ignore filename dirname basename
while getopts "r:" flag; do
case $flag in
r) ignore=;;
\?) exit 1;
esac
done
shift `expr $OPTIND - 1`
if [ -z "$1" ]; then
echo "No filenames given."
exit 1
fi
for filename in "$@"; do
dirname=`dirname $filename`
basename=`basename $filename`
sane_filename $dirname $basename
if [ ! -f $filename ]; then
echo "$filename does not exist." 1>&2
exit 1
fi
ctco=$dirname/.ctco.$basename
if [ ! -f $ctco ]; then
echo "$filename is not checked out." 1>&2
exit 1
fi
get_highest_version $dirname $basename
last=$dirname/.ct$version.$basename
lastcomment=$dirname/.ct${version}comment.$basename
mv -f $last $filename
rm -f $lastcomment
rm $ctco
touch $filename # For emacs, and correct make behavior.
done
}
function ctci {
local filename dirname basename
local comment
while getopts c:n: flag; do
case $flag in
c) comment="$OPTARG";;
n) comment="";;
\?) exit 1;
esac
done
shift `expr $OPTIND - 1`
if [ -z "$1" ]; then
echo "No filenames given."
exit 1
fi
for filename in "$@"; do
dirname=`dirname $filename`
basename=`basename $filename`
sane_filename $dirname $basename
if [ ! -f $filename ]; then
echo "$filename does not exist." 1>&2
exit 1
fi
ctco=$dirname/.ctco.$basename
if [ ! -f $ctco ]; then
echo "$filename is not checked out." 1>&2
exit 1
fi
# We touch the file, so emacs will note that it has changed.
touch $filename
chmod uga-w $filename
rm $ctco
done
}
function ctrevert {
local filename dirname basename
while getopts "" flag; do
case $flag in
\?) exit 1;
esac
done
shift `expr $OPTIND - 1`
if [ -z "$1" ]; then
echo "No filenames given."
exit 1
fi
for filename in "$@"; do
dirname=`dirname $filename`
basename=`basename $filename`
sane_filename $dirname $basename
if [ ! -f $filename ]; then
echo "$filename does not exist." 1>&2
exit 1
fi
ctco=$dirname/.ctco.$basename
if [ -f $ctco ]; then
echo "$filename is currently checked out. Use neartool unco $filename." 1>&2
exit 1
fi
get_highest_version $dirname $basename
if [ $version -eq -1 ]; then
echo "$filename has no versions." 1>&2
exit 1
fi
last=$dirname/.ct$version.$basename
lastcomment=$dirname/.ct${version}comment.$basename
mv -f $last $filename
rm -f $lastcomment
if [ $version -eq 0 ]; then
ctnew=$dirname/.ctnew.$basename
if [ -f $ctnew ]; then
echo "Removing newly created element $filename." 1>&2
rm -f $ctnew $filename
fi
fi
done
}
function ctrevertall {
local filename dirname basename
while getopts "" flag; do
case $flag in
\?) exit 1;
esac
done
shift `expr $OPTIND - 1`
if [ -z "$1" ]; then
echo "No filenames given."
exit 1
fi
for filename in "$@"; do
dirname=`dirname $filename`
basename=`basename $filename`
sane_filename $dirname $basename
if [ ! -f $filename ]; then
echo "$filename does not exist." 1>&2
exit 1
fi
ct0=$dirname/.ct0.$basename
if [ ! -f $ct0 ]; then
echo "$filename has no versions." 1>&2
else
ctnew=$dirname/.ctnew.$basename
if [ ! -f $ctnew ]; then
# If we didn't newly create this file, preserve its original version.
mv -f $ct0 $filename
fi
rm_all_ctinternals $dirname $basename
fi
done
}
function ctcollapse {
local filename dirname basename
while getopts "" flag; do
case $flag in
\?) exit 1;
esac
done
shift `expr $OPTIND - 1`
if [ -z "$1" ]; then
echo "No filenames given."
exit 1
fi
for filename in "$@"; do
dirname=`dirname $filename`
basename=`basename $filename`
sane_filename $dirname $basename
if [ ! -f $filename ]; then
echo "$filename does not exist." 1>&2
exit 1
fi
rm_all_ctinternals $dirname $basename
chmod uga-w $filename
touch $filename # For Emacs' benefit.
done
}
function ctrmname {
local filename dirname basename
while getopts "" flag; do
case $flag in
\?) exit 1;
esac
done
shift `expr $OPTIND - 1`
if [ -z "$1" ]; then
echo "No filenames given."
exit 1
fi
for filename in "$@"; do
dirname=`dirname $filename`
basename=`basename $filename`
sane_filename $dirname $basename
if [ ! -f $filename ]; then
echo "$filename does not exist." 1>&2
exit 1
fi
ctnew=$dirname/.ctnew.$basename
if [ ! -f $ctnew ]; then
# The filename exists on the actual Clearcase vobs. We need to
# record the removing.
get_rel_dir $projroot $dirname
local root=$rel_dir
echo "ctco -nc $root" >> $projroot/.ctcmds
echo "ctrm $root/$basename" >> $projroot/.ctcmds
fi
# Now remove all of the versioned stuff.
ctcollapse $filename
# And remove the file itself.
rm -f $filename
done
}
function ctfind {
local root=$1
if [ -z "$root" ]; then
echo "Specify a starting point of the find." 1>&2
exit 1
fi
find $root -name .ct0.\* -print | sed 's/\.ct0\.//'
}
function ctdescribe {
local filename dirname basename
while getopts "" flag; do
case $flag in
\?) exit 1;
esac
done
shift `expr $OPTIND - 1`
if [ -z "$1" ]; then
echo "No filenames given."
exit 1
fi
for filename in `ls "$@"`; do
dirname=`dirname $filename`
basename=`basename $filename`
ctco=$dirname/.ctco.$basename
get_highest_version $dirname $basename
version=`expr $version + 1`
if [ -f $ctco ]; then
if [ $version -eq 0 ]; then
echo "$filename is checked out with no versions."
else
echo "$filename is checked out as version $version."
list_comments $dirname $basename
fi
else
if [ $version -eq 0 ]; then
echo "$filename has not been checked out and has no versions."
else
echo "$filename is checked in as version $version."
list_comments $dirname $basename
fi
fi
done
}
function ctlsco {
local filename dirname basename
local ignore
while getopts "l" flag; do
case $flag in
l) ignore=;;
\?) exit 1;
esac
done
shift `expr $OPTIND - 1`
for filename in `ls "$@"`; do
dirname=`dirname $filename`
basename=`basename $filename`
ctco=$dirname/.ctco.$basename
if [ -f $ctco ]; then
ctdescribe $filename
fi
done
}
function ctdiff {
local filename dirname basename
local graphical
local ignore
local diff
while getopts "gp:" flag; do
case $flag in
g) graphical=y;;
p) ignore=;;
\?) exit 1;
esac
done
shift `expr $OPTIND - 1`
diff=diff
if [ $graphical ]; then
# The user requested a graphical difference; use gdiff if we can
# find it. Otherwise, fall back to diff.
if which gdiff 2>/dev/null >/dev/null; then
diff=gdiff
fi
fi
if [ -z "$1" ]; then
echo "No filenames given."
exit 1
fi
for filename in "$@"; do
dirname=`dirname $filename`
basename=`basename $filename`
sane_filename $dirname $basename
if [ ! -f $filename ]; then
echo "$filename does not exist." 1>&2
exit 1
fi
get_highest_version $dirname $basename
if [ $version -eq -1 ]; then
echo "$filename has no versions." 1>&2
exit 1
fi
last=$dirname/.ct$version.$basename
# First, call diff (the real diff) just to see if the files are
# different at all.
if diff $last $filename >/dev/null; then
echo Files are identical.
else
# If the files ARE different, then invoke diff again (which
# might be gdiff, this time) to actually report the differences.
# We must do it twice like this because gdiff might not return
# non-zero if the files are different.
$diff $last $filename
fi
done
}
function usage {
sed '/#ENDCOMMENT/,$d' <$0 >&2
exit 1
}
#
# Main entry point
#
command=$1
if [ -z "$command" ]; then
usage
fi
shift
projroot=`ctproj -r`
if [ -z "$projroot" ]; then
echo "Not currently in a project tree." 1>&2
exit 1
fi
case $command in
mkelem) ctmkelem "$@";;
mkdir) ctmkdir "$@";;
mv) ctmv "$@";;
co|checkout) ctco "$@";;
unco|uncheckout) ctunco "$@";;
ci|checkin) ctci "$@";;
revert) ctrevert "$@";;
revertall) ctrevertall "$@";;
collapse) ctcollapse "$@";;
rmname) ctrmname "$@";;
find) ctfind "$@";;
lsco|lscheckout) ctlsco "$@";;
describe) ctdescribe "$@";;
diff) ctdiff "$@";;
xdiff) ctdiff -g "$@";;
h|help|-h) usage;;
*) echo "Invalid option: $command" 1>&2
exit 1;
esac