# 
# delta-patch.sh: compute and apply an arbitrary patch 
################################################################
# Copyright (C) 2001, 2002 Tom Lord
# 
# See the file "COPYING" for further information about
# the copyright and warranty status of this work.
# 

set -e 
command_line="$*"

################################################################
# special options
# 
# Some options are special:
# 
#       --version | -V
#       --help | -h
# 
if test $# -ne 0 ; then

  for opt in "$@" ; do
    case $opt in

      --version|-V) exec larch --version
                    ;;


      --help|-h)
                printf "compute and apply an arbitrary patch\\n"
                printf "usage: delta-patch [options] from to upon output-dir\\n"
                printf "usage: delta-patch [options] --in-place from to upon\\n"
                printf "\\n"
                printf " -V --version                  print version info\\n"
                printf " -h --help                     display help\\n"
                printf "\\n"
                printf " -R --root root                specify the local archive root\\n"
                printf " -A --archive archive          specify the archive name\\n"
                printf "\\n"
                printf " --in-place                    modify an existing directory\\n"
                printf "\\n"
                printf "Construct a patch from FROM to TO and apply to UPON creating\\n"
                printf "OUTPUT-DIR\\n"
                printf "\\n"
                printf "FROM, TO, and UPON may each be revisions or project trees.\\n"
                printf "\\n"
                printf "If UPON is a project tree, the \"--in-place\" option can be used\\n"
                printf "to modify that tree directly, rather than creating a new tree.\\n"
                printf "\\n"
                printf "The exit status reflects the exit status of \"dopatch\": 0 for a\\n"
                printf "conflictless patch application, 1 if conflicts occurred, 2 if an\\n"
                printf "internal error occured.\\n"
                printf "\\n"
                exit 0
                ;;

      *)
                ;;
    esac
  done
fi

################################################################
# Ordinary Options
# 
# 

archroot=
archive=

list=
in_place=

quiet=--quiet
report=--report
verbose=
silent_opt=
quiet_opt=
report_opt=
verbose_opt=
debug_opt=


while test $# -ne 0 ; do

  case "$1" in 

    --silent)   shift
                quiet=
                report=
                verbose=
                silent_opt=--silent
                quiet_opt=
                report_opt=
                verbose_opt=
                ;;

    --quiet)    shift
                quiet=--quiet
                report=
                verbose=
                silent_opt=
                quiet_opt=--quiet
                report_opt=
                verbose_opt=
                ;;

    --report)   shift
                quiet=--quiet
                report=--report
                verbose=
                silent_opt=
                quiet_opt=
                report_opt=--report
                verbose_opt=
                ;;

    --verbose)  shift
                quiet=--quiet
                report=--report
                verbose=--verbose
                silent_opt=
                quiet_opt=
                report_opt=
                verbose_opt=--verbose
                ;;

    --debug)    shift
                larch heading "delta-patch: debugging output enabled\\n"
                set -x
                debug_opt=--debug
                ;;


    -R|--root)          shift
                        if test $# -eq 0 ; then
                          printf "delta-patch: -R and --root require an argument\\n" 1>&2
                          printf "try --help\\n" 1>&2
                          exit 2
                        fi
                        archroot="$1"
                        shift
                        ;;

    -A|--archive)       shift
                        if test $# -eq 0 ; then
                          printf "delta-patch: -A and --archive require an argument\\n" 1>&2
                          printf "try --help\\n" 1>&2
                          exit 2
                        fi
                        archive="$1"
                        shift
                        ;;

    --in-place)         shift
                        in_place=--in-place
                        ;;

    --)                 shift
                        break
                        ;;
                        
    -*)                 printf "delta-patch: unrecognized option (%s)\\n" "$1" 1>&2
                        printf "try --help\\n" 1>&2
                        exit 2
                        ;;

    *)                  break
                        ;;
  esac

done



################################################################
# Ordinary Arguments
# 

if test ! -z "$in_place" ; then
  n_dir_args=0
else
  n_dir_args=1
fi

n_args=$(($n_dir_args + 3))

if test $# -ne $n_args ; then
  printf "usage: delta-patch [options] from to upon output-dir\\n" 1>&2
  printf "       delta-patch [options] --in-place from to upon\\n" 1>&2
  printf "try --help\\n" 1>&2
  exit 1
fi

A="$1"
shift

B="$1"
shift

C="$1"
shift

if test -z "$in_place" ; then
  output_dir="$1"
  shift
else
  output_dir="$C"
fi

################################################################
# Sanity Check and Process Defaults
# 

here="`pwd`"

abs_dir()
{
  cd "$here"
  cd "`dirname \"$1\"`"
  printf "%s\\n" "`pwd`/`basename \"$1\"`"
}


if test -z "$in_place" -a -e "$output_dir" ; then
  printf "\\n" 1>&2
  printf "delta-patch: output directory already exists\\n" 1>&2
  printf "  dir %s\\n" "$output_dir"1>&2
  printf "\\n" 1>&2
  printf "Do you mean \"--in-place\"?\\n" 1>&2
  printf "\\n" 1>&2
  printf "Try \"delta-patch --help\"\\n" 1>&2
  printf "\\n" 1>&2
  exit 2
fi

output_dir="`abs_dir \"$output_dir\"`"

if larch valid-package-name --tolerant "$A" ; then

  A_is=revision

elif test -d "$A" ; then

  A_is=tree
  A="`abs_dir \"$A\"`"

else

  set +e
  larch valid-package-name --tolerant --errname delta-patch "$A"
  exit 2

fi

if larch valid-package-name --tolerant "$B" ; then

  B_is=revision

elif test -d "$B" ; then

  B_is=tree
  B="`abs_dir \"$B\"`"

else

  set +e
  larch valid-package-name --tolerant --errname delta-patch "$B"
  exit 2

fi

if test -z "$in_place" && larch valid-package-name --tolerant "$C" ; then

  C_is=revision

elif test -d "$C" ; then

  C_is=tree
  C="`abs_dir \"$C\"`"


elif test -z "$in_place" ; then

  set +e
  larch valid-package-name --tolerant --errname delta-patch "$C"
  exit 2

else

  printf "\\n" 1>&2
  printf "delta-patch: output directory does not exist\\n" 1>&2
  printf "  dir %s\\n" "$output_dir"1>&2
  printf "\\n" 1>&2
  printf "Try \"delta-patch --help\"\\n" 1>&2
  printf "\\n" 1>&2
  exit 2

fi


################################################################
# Greetings
# 

if test "(" -z "$__restart" -a ! -z "$quiet" ")" -o ! -z "$report" ; then
  larch heading "delta-patch\\n"
  printf "arguments: %s\\n"  "$command_line" | fold -w 60 | larch body-indent
  larch heading --sub "delta-patch start time: %s\\n" "`date`"
  larch heading --sub "from: %s" "$A" 
  larch heading --sub "to: %s" "$B" 
  larch heading --sub "upon: %s" "$C" 
  if test -z "$in_place" ; then
    larch heading --sub "output to: %s\\n" "$output_dir"
  else
    larch heading --sub "NOTE: modifying tree in place (--in-place)\\n"
    larch heading --sub --sub "tree: %s\\n" "$C"
  fi
fi


################################################################
# Make a Temporary Directory
# 

tmpdirparent="`pwd`"
tmpdir="$tmpdirparent/,,delta-patch.$$"

bail()
{
  cd "$tmpdirparent"
  rm -rf "$tmpdir"
  exit 2
}

trap "printf \"delta-patch: interrupted -- cleaning up\\n\" 1>&2 ; bail" INT
rm -rf "$tmpdir"
mkdir "$tmpdir"

################################################################
# Find or Make Trees for From and To
# 

if test "$A_is" = tree ; then
  A_tree="$A"

elif larch library-find -A "$archive" "$A" > /dev/null 2>&1 ; then

  A_tree="`larch library-find -A "$archive" "$A"`"

else

  cd "$tmpdir"
  mkdir A
  cd A
  larch nested larch build-revision --cache "$here" -R "$archroot" -A "$archive" -- "$A"
  A_tree="$tmpdir/A"

fi

if test "$B_is" = tree ; then

  B_tree="$B"

elif larch library-find -A "$archive" "$B" > /dev/null 2>&1 ; then

  B_tree="`larch library-find -A "$archive" "$B"`"

else

  cd "$tmpdir"
  mkdir B
  cd B
  larch nested larch build-revision --cache "$here" -R "$archroot" -A "$archive" -- "$B"
  B_tree="$tmpdir/B"

fi

################################################################
# Compute the Patch Set
# 

cd "$tmpdir" 
larch nested larch mkpatch "$A_tree" "$B_tree" the-patch

patch="$tmpdir/the-patch"

################################################################
# Establish the Output Directory
# 

if test -z "$in_place" ; then

  if test "$C_is" = tree ; then

    mkdir "$output_dir"
    cd "$C"
    larch inventory --source --precious --all --nested \
    | awk '{ print $2 }' \
    | copy-file-list -- - . "$output_dir"

  else

    larch nested larch get -R "$archroot" -A "$archive" -- "$C" "$output_dir"

  fi

fi

################################################################
# Apply the Patch
# 

set +e
larch nested larch dopatch "$patch" "$output_dir"
patch_status=$?
set -e

################################################################
# Bye (with Patch Status)
# 

cd "$tmpdirparent"
rm -rf "$tmpdir"

if test $patch_status -eq 1 -a ! -z "$quiet" ; then
  larch heading --sub "NOTICE: merge conflicts occured\\n"
  printf "  Look for \".rej\" files in %s\\n" "`basename \"$output_dir\"`" | larch body-indent --sub
fi

if test ! -z "$quiet" ; then
  larch heading --sub "delta-patch finish time: %s\\n" "`date`"
fi

exit "$patch_status"

# tag: Tom Lord Sat Jan  5 12:49:36 2002 (branching-and-merging/delta-patch.sh)
#
