#! /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
