#!/bin/sh
# dopatch.sh - apply a patch directory (from mkpatch)
# 
################################################################
# 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 "apply a patch tree to a source tree\\n"
		printf "usage: dopatch [options] patchdir srcdir\\n"
		printf "\\n"
		printf " -h --help                     display help\\n"
		printf " -V --version                  print version info\\n"
		printf "\\n"
		printf " --silent                      no output (except odd errors)\\n"
		printf " --quiet                       brief output\\n"
		printf " --report                      default output\\n"
		printf " --verbose                     maximal output\\n"
		printf " --debug                       debugging output\\n"
		printf " --delete-removed              don't save removed files\\n"
		printf "\\n"
		printf " --explicit                    force the explicit tagging method\\n"
		printf " --implicit                    force the implicit tagging method\\n"
		printf " --names                       force the name-based tagging method\\n"
		printf "\\n"
		printf " --forward                     pass the --forward option to patch\\n"
		printf "\\n"
		printf "Apply the patches in PATCHDIR to the source tree SRCDIR.\\n"
		printf "\\n"
		printf "See also \"mkpatch --help\".\\n"
		printf "\\n"
		exit 0
      		;;

      *)
		;;
    esac
  done
fi

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

quiet=--quiet
report=--report
verbose=
debug=
method=
forward=

delete_removed=

while test "$#" -gt 0 ; do

  case "$1" in

    --forward)		shift
  			forward=--forward
			;;

    --explicit)		shift
  			method=--explicit
			;;

    --implicit)		shift
			method=--implicit
			;;

    --names)		shift	
	    		method=--names
			;;

    --silent)	shift
    		quiet=
		report=
		verbose=
		;;

    --quiet)	shift
    		quiet=--quiet
		report=
		verbose=
		;;

    --report)	shift
    		quiet=--quiet
		report=--report
		verbose=
		;;

    --verbose)	shift
    		quiet=--quiet
		report=--report
		verbose=--verbose
		;;

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

    --delete-removed)	shift
    			delete_removed=--delete-removed
			;;

    --)		shift
    		break
		;;

    -*)		printf "mkpatch: unrecognized option (%s)\\n" "$1" 1>&2
    		printf "\\n" 1>&2
		printf "Try \"larch mkpatch --help\"\\n" 1>&2
		exit 1
		;;

    *)		break
		;;

  esac


done

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

if test $# != 2 ; then
  printf "usage: dopatch [options] patchdir srcdir\\n" 1>&2
  printf "try --help\\n"  
  exit 2
fi

delta="$1"
tree="$2"

################################################################
# Make the directory names absolute
# 
start="`pwd`"

cd "$delta"
delta="`pwd`"

cd "$start"
cd "$tree"
tree="`pwd`"

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

if test ! -z "$quiet" ; then
  larch heading "dopatch\\n"
  printf "arguments: %s\\n" "$command_line" | fold -w 60 | larch body-indent
  larch heading --sub "dopatch start time: %s\\n" "`date`"
  larch heading --sub "project tree: %s\\n" "$tree"
  larch heading --sub "delta: %s\\n" "$delta"
fi


################################################################
# Create a Place to Save Removed Files and Directories
# 

if test -z "$delete_removed" ; then
  archive="$tree/++removed-by-dopatch-`basename \"$patchdir\"`--`date +%Y%m%d.%H:%M:%S`"
  mkdir "$archive"

  if test ! -z "$quiet" ; then
    larch heading --sub "files removed by patch will be saved\\n"
    printf "%s\\n" "$archive" | larch body-indent --sub
  fi

else
  if test ! -z "$quiet" ; then
    larch heading --sub "files removed by patch will be deleted\\n"
  fi
fi


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

tmpdir="$tree/,,dopatch.$$"

bail()
{
  cd "$tree"
  rm -rf "$tmpdir"
  exit 1
}

finish()
{
  cd "$tree"
  rm -rf "$tmpdir"
  exit 0
}

trap "printf \"dopatch: interrupted -- cleaning up\\n\" 1>&2 ; bail" INT

mkdir "$tmpdir"


################################################################
# Compute Many Things
# 

if test ! -z "$verbose" ; then
  larch heading --sub "inventory the tree being patched\\n"
  larch heading --sub --sub "start time: %s\\n" "`date`"
fi

# inventory tree
# 
tree_dir_index="$tmpdir/,,tree-dir-index"
tree_file_index="$tmpdir/,,tree-file-index"

larch inventory $method --source --all --directories --tags | sort -k 2 > "$tree_dir_index"
larch inventory $method --source --all --files --tags | sort -k 2 > "$tree_file_index"

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

# provide alternate sorts of mod indexes
# 
mod_file_index_by_name="$tmpdir/,,mod-file-index-by-name"
mod_dir_index_by_name="$tmpdir/,,mod-dir-index-by-name"

sort -k 1 "$delta/mod-files-index" > "$mod_file_index_by_name"
sort -k 1 "$delta/mod-dirs-index" > "$mod_dir_index_by_name"

original_only_file_ids="$tmpdir/,,original-only-file-ids"
modified_only_file_ids="$tmpdir/,,modified-only-file-ids"
original_only_dir_ids="$tmpdir/,,original-only-dir-ids"
modified_only_dir_ids="$tmpdir/,,modified-only-dir-ids"

join -v 1 -o 1.2 -1 2 -2 2 "$delta/orig-files-index" "$delta/mod-files-index" \
> "$original_only_file_ids"

join -v 1 -o 1.2 -1 2 -2 2 "$delta/mod-files-index" "$delta/orig-files-index" \
> "$modified_only_file_ids"

join -v 1 -o 1.2 -1 2 -2 2 "$delta/orig-dirs-index" "$delta/mod-dirs-index" \
> "$original_only_dir_ids"

join -v 1 -o 1.2 -1 2 -2 2 "$delta/mod-dirs-index" "$delta/orig-dirs-index" \
> "$modified_only_dir_ids"


# split removed files list from patch into those files
# that are in the tree vs. those that are missing
# 
removed_files_index="$tmpdir/,,removed-files-index"
missing_removed_files_index="$tmpdir/,,missing-removed-files-index"

join -o 2.1,2.2 -1 1 -2 2 "$original_only_file_ids" "$tree_file_index" \
> "$removed_files_index"

join -v 1 -o 1.1 -1 1 -2 2 "$original_only_file_ids" "$tree_file_index" \
| join -o 2.1,2.2 -1 1 -2 2 - "$delta/orig-files-index" \
> "$missing_removed_files_index"



# various lists about renamed files
# 
renamed_files="$tmpdir/,,renamed-files"
renamed_file_ids="$tmpdir/,,renamed-file-ids"
renamed_files_index="$tmpdir/,,renamed-files-index"
missing_renamed_files_index="$tmpdir/,,missing-renamed-files-index"

awk -f "$ARCH_SUBCMD_ROOT/patch-sets/compute-renamed.awk" \
       "$delta/orig-dirs-index" "$delta/mod-dirs-index" \
       "$delta/orig-files-index" "$delta/mod-files-index" \
| cut -s -d ' ' -f 1,2 \
| sort -k 2,2 \
> "$renamed_files"

join -o 2.2 -1 2 -2 1 "$renamed_files" "$mod_file_index_by_name" \
| sort \
> "$renamed_file_ids"

join -o 2.1,2.2 -1 1 -2 2 "$renamed_file_ids" "$tree_file_index" \
> "$renamed_files_index"

join -v 1 -o 1.1 -1 1 -2 2 "$renamed_file_ids" "$tree_file_index" \
| join -o 2.1,2.2 -1 1 -2 2 - "$delta/orig-files-index" \
> "$missing_renamed_files_index"

renamed_dirs="$tmpdir/,,renamed-dirs"
renamed_dir_ids="$tmpdir/,,renamed-dir-ids"
renamed_dirs_index="$tmpdir/,,renamed-dirs-index"
missing_renamed_dirs_index="$tmpdir/,,missing-renamed-dirs-index"
removed_dirs_index="$tmpdir/,,removed-dirs-index"
missing_removed_dirs_index="$tmpdir/,,missing-removed-dirs-index"
shuffled_dirs_temp_names="$tmpdir/,,shuffled-dirs-temp-names"
shuffled_dirs_temp_names_forward_sort="$tmpdir/,,shuffled-dirs-temp-names-forward-sort"
shuffled_dirs_new_names="$tmpdir/,,shuffled-dirs-new-names"
shuffled_removed_dir_names="$tmpdir/,,shuffled-removed-dir-names"

awk -f "$ARCH_SUBCMD_ROOT/patch-sets/compute-renamed.awk" \
       "$delta/orig-dirs-index" "$delta/mod-dirs-index" \
       "$delta/orig-dirs-index" "$delta/mod-dirs-index" \
| cut -s -d ' ' -f 1,2 \
| sort -k 2,2 \
> "$renamed_dirs"

join -o 2.2 -1 2 -2 1 "$renamed_dirs" "$mod_dir_index_by_name" \
| sort \
> "$renamed_dir_ids"

join -o 2.1,2.2 -1 1 -2 2 "$renamed_dir_ids" "$tree_dir_index" \
> "$renamed_dirs_index"

join -v 1 -o 1.1 -1 1 -2 2 "$renamed_dir_ids" "$tree_dir_index" \
| join -o 2.1,2.2 -1 1 -2 2 - "$delta/orig-dirs-index" \
> "$missing_renamed_dirs_index"

join -o 2.1,2.2 -1 1 -2 2 "$original_only_dir_ids" "$tree_dir_index" \
> "$removed_dirs_index"

join -v 1 -o 1.1 -1 1 -2 2 "$original_only_dir_ids" "$tree_dir_index" \
| join -o 2.1,2.2 -1 1 -2 2 - "$delta/orig-dirs-index" \
> "$missing_removed_dirs_index"

cat "$renamed_dirs_index" "$removed_dirs_index" \
| cut -s -d ' ' -f 1 \
| sort -r \
| awk ' BEGIN { counter = 0 }
        {
	  print  $1 "\t" counter ;
	  counter = counter + 1
	} ' \
> "$shuffled_dirs_temp_names"

sort -k 1 "$shuffled_dirs_temp_names" > "$shuffled_dirs_temp_names_forward_sort"

join -o 1.1,2.1 -1 2 -2 2 "$renamed_dirs_index" "$delta/mod-dirs-index" \
| sort -k 1 \
| join -o 1.2,2.2 -1 1 -2 1  - "$shuffled_dirs_temp_names_forward_sort" \
| sort -k 2 \
> "$shuffled_dirs_new_names"

cat  "$removed_dirs_index" \
| sort -k 1 \
| join -o 1.1,2.2 -1 1 -2 1 - "$shuffled_dirs_temp_names_forward_sort" \
| sort -k 1 \
> "$shuffled_removed_dir_names"

needed_new_directories="$tmpdir/,,needed-new-directories"
install_dirs_plan_raw="$tmpdir/,,install-dirs-plan-raw"
dirs_moved_in_tree="$tmpdir/,,dirs_moved_in_tree"
install_dirs_plan_cooked="$tmpdir/,,install-dirs-plan-cooked"
dirs_moved_sed_script="$tmpdir/,,dirs_moved_sed_script"

join -v 1 -o 1.1 -1 1 -2 2 "$modified_only_dir_ids" "$tree_dir_index" \
| join -o 2.1 -1 1 -2 2 - "$delta/mod-dirs-index" \
| sort \
> "$needed_new_directories"

( sed -e "s/\$/ c --/" "$needed_new_directories" ; \
  (  cat "$shuffled_dirs_new_names" \
   | sort -k 1 \
   | join -o 1.1,1.2,2.2 -1 1 -2 1 - "$mod_dir_index_by_name" ) ) \
| ( sed -e "s/\\([^ ]*\\)\\(.*\\) \\([^ ]*\\)$/\\1 \\2 \\1 \\3/" \
        -e "s,\\([^ ]*\\)/\\([^ /]*\\),\\1/ \\2," )\
| sort -k 1 -k 2 \
> "$install_dirs_plan_raw"  

join -o 1.1,2.1 -1 2 -2 1 "$delta/mod-dirs-index"  "$tree_dir_index" \
| awk '{ if ($1 != $2) print $1 "\t" $2 }' \
| sort -k 1 -k 2 \
> "$dirs_moved_in_tree"

( sed -e '{
	    s,[:.$*[\\],\\&,g
	    s/^/s:^/
            s,\$, ./:,
	    s!/\\{0,1\\}[	]!/:!
            s/$/\
		t/
 	  }' \
    "$dirs_moved_in_tree" ; \
  printf 's:^./:. ./:' \
) \
> "$dirs_moved_sed_script"

rm -f "$tmpdir/,tmp"
sed -f "$dirs_moved_sed_script" "$install_dirs_plan_raw" \
> "$tmpdir/,tmp"

( cat "$tmpdir/,tmp" \
  | sort -k 6 \
  | join -o 1.1,1.2,1.3,1.4,1.5,2.1 -1 6 -2 2 \
          - "$tree_dir_index" ; \
  cat "$tmpdir/,tmp" | grep -E -e '--$' ) \
| sort -k 2 \
> "$install_dirs_plan_cooked"

needed_new_files_raw="$tmpdir/,,needed-new-files-raw"
needed_new_files_cooked="$tmpdir/,,needed-new-files-cooked"
needed_new_files_script="$tmpdir/,,needed-new-files-script"

join -v 1 -o 1.1 -1 1 -2 1 "$modified_only_file_ids" "$tree_file_index" \
| join -o 2.1 -1 1 -2 2 - "$delta/mod-files-index" \
| sort \
| sed -e 's,\(.*\)/\([^/]*\),\1/ \2 &,' \
> "$needed_new_files_raw"

sed -f "$dirs_moved_sed_script" "$needed_new_files_raw" \
> "$needed_new_files_cooked"

cat "$needed_new_files_cooked" \
| awk -v source="$delta/new-files-archive" \
      -v dest="$tree" \
      '{
         if ($2 == "./")
	    rel_path= "/"
	 else
	    rel_path= "/" substr($2,3) 

         print "printf \"%s\\\\n\" \"" $1 rel_path $3 "\""
	 print "if test -h \"" source "/" $4 "\" ; then"
	 print "  ln -s \"`read-link " source "/" $4 "`\" \"" dest "/" $1 rel_path $3 "\""
	 print "else"
	 print "  cp -p \"" source "/" $4 "\" \"" dest "/" $1 rel_path $3 "\""
	 print "fi"
       }' \
> "$needed_new_files_script"

install_dirs_soft_conflicts="$tmpdir/,,install-dirs-soft-conflicts"
install_dirs_hard_conflicts="$tmpdir/,,install-dirs-hard-conflicts"

cat "$install_dirs_plan_cooked" \
| awk '{
	 n_components = split (substr($2,3), components, "/");
	 path = "" ; 
	 for ( x = 1 ; x <= n_components ; ++x )
	   {
	     path = path components[x] "/";
	     print $1 "/" path ;
	   }
	 if ( $4 == "c" )
	   print $1 "/" $2 "/" $3
       } ' \
| sort -r -u \
> "$install_dirs_soft_conflicts"

cat "$install_dirs_plan_cooked" \
| awk '{
	 if ( $4 != "c" )
	   {
             if ($2 == "./")
	       rel_path= "/"
	     else
	       rel_path= "/" substr($2,3) 

	     print $1 rel_path $3
	   }
       } ' \
| sort -r -u \
> "$install_dirs_hard_conflicts"


installed_dirs_script="$tmpdir/,,installed-dirs-script"

cat "$install_dirs_plan_cooked" \
| awk -v shuffle="$tmpdir/shuffled-dirs" \
      '{
         if ($2 == "./")
	    rel_path= "/"
	 else
	    rel_path= "/" substr($2,3)

	 if ($4 == "c")
	   {
	     print "printf \"%s\\\\n\" \"" $1 rel_path $3 "\""
	     print "mkdir -p \"" $1 rel_path $3 "\""
	   }
	 else
	   {
  	     print "printf \"%s\\\\n => %s\\\\n\" \"" $6 "\" \"" $1 rel_path $3 "\""
	     print "mkdir -p \"" $1 rel_path "\""
	     print "mv \"" shuffle "/" $4 "\" \"" $1 rel_path $3 "\""
	   }
       }' \
> "$installed_dirs_script"

removed_dirs_script="$tmpdir/,,removed-dirs-script"

if test -z "$delete_removed" ; then
  cat "$shuffled_removed_dir_names" \
  | awk -v shuffle="$tmpdir/shuffled-dirs" \
        -v archive="$archive" \
        '{
	   print "printf \"%s\\\\n\" \"" $1 "\""
           print "mkdir -p \"" archive "/`dirname \\\"" $1 "\\\"`\"" ;
	   print "mv \"" shuffle "/" $2 "\" \"" archive "/" $1 "\""
         }' \
  > "$removed_dirs_script"
fi


renamed_files_plan_raw="$tmpdir/,,renamed-files-plan-raw"
renamed_files_plan_cooked="$tmpdir/,,renamed-files-plan-cooked"

join -o 2.1 -1 1 -2 2 "$renamed_file_ids" "$delta/mod-files-index" \
| sort \
| sed -e 's,\(.*\)/\(.*\),\1/ \2 &,' \
> "$renamed_files_plan_raw"

sed -f "$dirs_moved_sed_script" "$renamed_files_plan_raw" \
> "$renamed_files_plan_cooked"


install_files_soft_dir_conflicts="$tmpdir/,,install-files-soft-dir-conflicts"
install_files_hard_conflicts="$tmpdir/,,install-files-hard-conflicts"

cat "$needed_new_files_cooked" "$renamed_files_plan_cooked" \
| awk '{
	 n_components = split (substr($2,3), components, "/");
	 path = "" ;
	 for ( x = 1 ; x <= n_components ; ++x )
	   {
	     path = path components[x] "/";
	     print $1 "/" path ;
	   }
        }' \
| sort -u  \
> "$install_files_soft_dir_conflicts"

cat "$needed_new_files_cooked" "$renamed_files_plan_cooked" \
| awk '{
         if ($2 == "./")
	    rel_path= "/"
	 else
	    rel_path= "/" substr($2,3) 

	 print $1 rel_path $3
        } ' \
| sort -u  \
> "$install_files_hard_conflicts"

renamed_files_mod_old_tree="$tmpdir/,,renamed-files-mod-old-tree"

join -o 2.1,1.1 -1 2 -2 2 "$renamed_files_index" "$delta/mod-files-index" \
| sort -k 1 \
> "$renamed_files_mod_old_tree"

renamed_files_plan_plated="$tmpdir/,,renamed-files-plan-plated"

join -o 2.1,2.2,2.3,1.2 -1 1 -2 4 \
     "$renamed_files_mod_old_tree" \
     "$renamed_files_plan_cooked" \
| sort -k 2 \
> "$renamed_files_plan_plated"



renamed_files_script="$tmpdir/,,renamed-files-script"

cat "$renamed_files_plan_plated" \
| awk -v source="$tmpdir/renamed-files" \
      -v tree="$tree" \
      '{
         if ($2 == "./")
	    rel_path= "/"
	 else
	    rel_path= "/" substr($2,3) 

	 print "printf \"%s\\\\n => %s\\\\n\" \"" $4 "\" \"" $1 rel_path $3 "\""

	 print "mv \"" source "/" $4 "\" \"" tree "/" $1 rel_path $3 "\""
       }' \
> "$renamed_files_script"


tree_file_renames="$tmpdir/,,tree-file-renames"
tree_dir_renames="$tmpdir/,,tree-dir-renames"

join -o 2.1,2.2,2.3,1.2 -1 1 -2 4 \
     "$mod_file_index_by_name" "$renamed_files_plan_cooked" \
| sort -k 4 \
| join -o 1.1,1.2,1.3,2.1 -1 4 -2 2 - "$tree_file_index" \
| awk '{
         if ($2 == "./")
	    rel_path= "/"
	 else
	    rel_path= "/" substr($2,3)

	 print $4 " " $1 rel_path $3
       }' \
| sort -r -k 1 \
> "$tree_file_renames"


cat "$install_dirs_plan_cooked" \
| awk '{
         if ($4 != "c")
	   {
	     if ($2 == "./")
	       rel_path= "/"
	     else
	       rel_path= "/" substr($2,3)

	     print $6 " " $1 rel_path $3
           }
       }' \
| sort -r -k 1 \
> "$tree_dir_renames"

tree_dir_renames_sed_script="$tmpdir/,,tree-dir-renames-sed-script"

cat "$tree_dir_renames" \
| sed -e '{
	    s,[:.$*[\\],\\&,g
	    s/^/s:^/
	    s, ,\\([/	]\\):,
	    s,$,\\1:\
		t,
	  }' \
> "$tree_dir_renames_sed_script"


rearranged_tree_dir_index="$tmpdir/,,rearranged-tree-dir-index"

join -v 1 -o 1.1,1.2 -1 2 -2 2 "$tree_dir_index" "$removed_dirs_index" \
| sed -e "s/ /	/" \
| sed -f "$tree_dir_renames_sed_script" \
> "$rearranged_tree_dir_index"


rearrangements_summary="$tmpdir/,,rearrangements-summary"

(  cat "$tree_dir_renames" "$tree_file_renames" \
   | sed -e 's/^/r /' ; \
   \
   cut -s -d ' ' -f 1 "$removed_dirs_index" \
   | sed -e 's/^/d /' ) \
| sort -r -k 2 \
> "$rearrangements_summary"

rearrangements_summary_sed_script=",,rearrangements-summary-sed-script"

tree_dir_and_file_renames_sed_script="$tmpdir/,,tree-dir-and-file-renames-sed-script"

cat "$rearrangements_summary" \
| sed -e 's,[/.$*[\\],\\&,g' \
      -e '/^r / { 
		  s,^r ,s/^,
		  s, ,\\([/	]\\)/,
		  s,$,\\1/\
		           t,
		}' \
      -e '/^d / {
		  s,^d ,/,
		  s,$,/d,
		}' \
> "$tree_dir_and_file_renames_sed_script"

rearranged_tree_file_index="$tmpdir/,,rearranged-tree-file-index"

join -v 1 -o 1.1,1.2 -1 2 -2 2 "$tree_file_index" "$removed_files_index" \
| sed -e "s/ /	/" \
| sed -f "$tree_dir_and_file_renames_sed_script" \
> "$rearranged_tree_file_index"


all_patches="$tmpdir/,,all-patches"

( cd "$delta/patches" ; find . -type f ) \
> "$all_patches"

patch_list="$tmpdir/,,patch-list"
patch_map="$tmpdir/,,patch-map"

grep -E -e '\.patch$' "$all_patches" \
| sed -e 's/\.patch$//' \
| sort \
> "$patch_list"

join -o 1.1,2.2 -1 1 -2 1 "$patch_list" "$mod_file_index_by_name" \
| sort -k 2 \
| join -o 2.1,1.1 -1 2 -2 2 - "$rearranged_tree_file_index" \
| sort -k 1 \
> "$patch_map"

link_patch_list="$tmpdir/,,link-patch-list"
link_patch_map="$tmpdir/,,link-patch-map"

grep -E -e '\.link-mod$' "$all_patches" \
| sed -e 's/.link-mod$//' \
| sort \
> "$link_patch_list"

join -o 1.1,2.2 -1 1 -2 1 "$link_patch_list" "$mod_file_index_by_name" \
| sort -k 2 \
| join -o 2.1,1.1 -1 2 -2 2 - "$rearranged_tree_file_index" \
| sort -k 1 \
> "$link_patch_map"

binary_patch_list="$tmpdir/,,binary-patch-list"
binary_patch_map="$tmpdir/,,binary-patch-map"

grep -E -e '\.modified$' "$all_patches" \
| sed -e 's/.modified$//' \
| sort \
> "$binary_patch_list"

join -o 1.1,2.2 -1 1 -2 1 "$binary_patch_list" "$mod_file_index_by_name" \
| sort -k 2 \
| join -o 2.1,1.1 -1 2 -2 2 - "$rearranged_tree_file_index" \
| sort -k 1 \
> "$binary_patch_map"


metadata_patch_list="$tmpdir/,,metadata-patch-list"
metadata_patch_map="$tmpdir/,,metadata-patch-list"

grep -E -e '\.meta-mod$' "$all_patches" \
| sed -e 's/\.meta-mod$//' \
| sort \
> "$metadata_patch_list"

join -o 1.1,2.2 -1 1 -2 1 "$metadata_patch_list" "$mod_file_index_by_name" \
| sort -k 2 \
| join -o 2.1,1.1 -1 2 -2 2 - "$rearranged_tree_file_index" \
| sort -k 1 \
> "$metadata_patch_map"

dir_metadata_patch_list="$tmpdir/,,dir-metadata-patch-list"
dir_metadata_patch_map="$tmpdir/,,dir-metadata-patch-list"

grep -E -e '^=dir-meta-mod$' "$all_patches" \
| sed -e 's,/=dir-meta-mod$,,' \
| sort \
> "$dir_metadata_patch_list"

join -o 1.1,2.2 -1 1 -2 1 "$dir_metadata_patch_list" "$mod_file_index_by_name" \
| sort -k 2 \
| join -o 2.1,1.1 -1 2 -2 2 - "$rearranged_tree_file_index" \
| sort -k 1 \
> "$dir_metadata_patch_map"

missing_file_patch_list="$tmpdir/,,missing-file-patch-list"

( \
  join -o 2.1,2.2 -1 1 -2 1 "$patch_list" "$mod_file_index_by_name" ; \
  join -o 2.1,2.2 -1 1 -2 1 "$link_patch_list" "$mod_file_index_by_name" ; \
  join -o 2.1,2.2 -1 1 -2 1 "$binary_patch_list" "$mod_file_index_by_name" ; \
  join -o 2.1,2.2 -1 1 -2 1 "$metadata_patch_list" "$mod_file_index_by_name" ; \
  join -o 2.1,2.2 -1 1 -2 1 "$dir_metadata_patch_list" "$mod_file_index_by_name" ; \
) \
| sort -k 2 -u \
| join -v 1 -o 1.1 -1 2 -2 2 - "$tree_file_index" \
| sed -e 's,/\([^/]*\)$, \1,' \
| awk "{
	 print \"find '\" \$1 \"' -name '\" \$2 \".*'\"
       }" \
| ( cd "$delta/patches" ; $ARCH_SHELL ) \
> "$missing_file_patch_list"


final_tree_file_index="$tmpdir/,,final-tree-file-index"
final_tree_dir_index="$tmpdir/,,final-tree-dir-index"

(  join -o 1.1,1.2,1.3,2.2 -1 4 -2 1 "$needed_new_files_cooked" "$mod_file_index_by_name" \
 | awk '{
          if ($2 == "./")
	    rel_path= "/"
	  else
	    rel_path= "/" substr($2,3)

          print $1 rel_path $3 "\t" $4
        }' ; \
 cat "$rearranged_tree_file_index" ) \
| sort -k 2 \
> "$final_tree_file_index"

(  cat "$install_dirs_plan_cooked" \
 | awk '{ if ($4 == "c") print $1 " " $2 " " $3 " " $5 }' \
 | join -o 1.1,1.2,1.3,2.2 -1 4 -2 1 - "$mod_dir_index_by_name" \
 | awk '{
          if ($2 == "./")
	    rel_path= "/"
	  else
	    rel_path= "/" substr($2,3)

          print $1 rel_path $3 "\t" $4
        }' ; \
 cat "$rearranged_tree_dir_index" ) \
| sort -k 2 \
> "$final_tree_dir_index"


################################################################
# Archive and Delete Removed Files
# 

if test ! -z "`head -n 1 \"$removed_files_index\"`" ; then
  if test ! -z "$report" ; then
    if test -z "$delete_removed" ; then 
      larch heading --sub "archive and delete removed files\\n"
    else
      larch heading --sub "delete removed files\\n"
    fi
    if test ! -z "$verbose" ; then
      larch heading --sub --sub "start time: %s\\n" "`date`"
    fi
  fi
  
  if test -z "$delete_removed" ; then
    cd "$tree" ;

    if test ! -z "$report" ; then
      if test ! -z "$verbose" ; then
        larch heading --sub --sub "file list\\n"
	verb_sub=--sub
      else
        verb_sub=
      fi
      cut -s -d ' ' -f 1 "$removed_files_index" \
      | sort \
      | tar -T - "-vcf" "$tmpdir/,,lost-files.tar" \
      | larch body-indent --sub $verb_sub
    else
      cut -s -d ' ' -f 1 "$removed_files_index" \
      | sort \
      | tar -T - "-cf" "$tmpdir/,,lost-files.tar"
    fi

  fi
  
  cd "$tree" 
  cut -s -d ' ' -f 1 "$removed_files_index" | xargs need-args 'rm -rf'
  
  if test ! -z "$verbose" ; then
    larch heading --sub --sub "finish time: %s\\n" "`date`"
  fi
fi

################################################################
# Set Aside Renamed Files
# 

if test ! -z "$verbose" ; then
  larch heading --sub "set aside renamed files\\n"
  larch heading --sub --sub "start time: %s\\n" "`date`"
fi

# set aside the renamed files
# 
mkdir "$tmpdir/renamed-files"

cut -s -d ' ' -f 1 "$renamed_files_index" \
| copy-file-list -- - "$tree" "$tmpdir/renamed-files"

# remove the renamed files
# 
cd "$tree"
cut -s -d ' ' -f 1 "$renamed_files_index" | xargs need-args 'rm -rf'

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

################################################################
# Set Aside Renamed and Removed Directories
# 

if test ! -z "$verbose" ; then
  larch heading --sub "set aside renamed and removed directories\\n"
  larch heading --sub --sub "start time: %s\\n" "`date`"
fi

mkdir "$tmpdir/shuffled-dirs"

OLD_IFS="$IFS"

IFS="
"
for pair in `cat "$shuffled_dirs_temp_names"` ; do

  IFS="$OLD_IFS"

  dir_name=${pair%	*}
  tmp_name=${pair##*	}

  mv "$tree/$dir_name" "$tmpdir/shuffled-dirs/$tmp_name"

done

IFS="$OLD_IFS"

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

################################################################
# How to Deal With Pre-existing .orig and .rej Files
# 

made_rej_files=

preserve_old_patch_spew()
{
  here="`pwd`"

  cd "$tree"
  file="$1"

  if test -h "$file.orig" -o -e "$file.orig" ; then
    if test -z "$delete_removed" ; then 
      mkdir -p "$archive/`dirname \"$file\"`"
      mv "$file.orig" "$archive/`dirname \"$file\"`"
    else
      rm "$file.orig"
    fi
  fi

  if test -h "$file.orig" -o -e "$file.rej" ; then
    if test -z "$delete_removed" ; then
      mkdir -p "$archive/`dirname \"$file\"`"
      mv "$file.rej" "$archive/`dirname \"$file\"`"
    else
      rm "$file.rej"
    fi
  fi

  cd "$here"
}

################################################################
# How to Deal With Conflicts for New and Renamed Directories
# 

deferred_conflict()
{
  made_rej_files=yes

  here="`pwd`"

  cd "$tree"
  conflict_file="$1"

  preserve_old_patch_spew "$conflict_file"

  mv "$1" "$1.orig"
  printf "%s\\n" "$conflict_file" >> "$tmpdir/,,deferred_conflicts"

  cd "$here"
}


################################################################
# Eliminate Directory Conflicts for New and Renamed Directories
# 


any_dir_conflicts=

dir_dir_conflicts_heading()
{
  if test ! -z "$report" ; then
    larch heading --sub "check for conflicts with new or renamed directories\\n"
  fi
  any_dir_conflicts=yes
}

cd "$tree"


for dir in `cat "$install_dirs_hard_conflicts"` ; do

  if test -h "$dir" -o  -e "$dir" ; then
    deferred_conflict "$dir"
    dir_dir_conflicts_heading
    if test ! -z "$report" ; then
      printf "%s\\n" "$dir" | larch body-indent --sub --nonl
    fi
  fi

done

for dir in `cat "$install_dirs_soft_conflicts"` ; do

  if test "(" -h "$dir" -o -e "$dir" ")" -a ! -d "$dir" ; then
    deferred_conflict "$dir"
    dir_dir_conflicts_heading
    if test ! -z "$report" ; then
      printf "%s\\n" "$dir" | larch body-indent --sub --nonl
    fi
  fi

done

if test ! -z "$report" -a ! -z "$any_dir_conflicts" ; then
  printf "\\n"
fi


################################################################
# Put the Renamed and New Directories In New Locations
# 

if test ! -z "$report" -a ! -z "`head -n 1 \"$installed_dirs_script\"`" ; then
  larch heading --sub "put the renamed and new directories in new locations\\n"
  cat "$installed_dirs_script" | $ARCH_SHELL | larch body-indent --sub
else
  cat "$installed_dirs_script" | $ARCH_SHELL > /dev/null
fi


################################################################
# Put Removed Dirs in the Archive
# 

if test -z "$delete_removed" ; then

  if test ! -z "$report" -a ! -z "`head -1 \"$removed_dirs_script\"`" ; then
    larch heading --sub "put removed dirs in the archive\\n"
    cat "$removed_dirs_script" | $ARCH_SHELL | larch body-indent --sub
  else
    cat "$removed_dirs_script" | $ARCH_SHELL > /dev/null
  fi

fi

################################################################
# Put Removed Files in the Archive
# 

if test -z "$delete_removed" -a ! -z "`head -n 1 \"$removed_files_index\"`" ; then

  cd "$archive"

  if test ! -z "$verbose" ; then
    larch heading --sub "put removed files in the archive\\n"
    tar -m -xvpf "$tmpdir/,,lost-files.tar" | larch body-indent --sub
  else
    tar -m -xpf "$tmpdir/,,lost-files.tar"
  fi

fi

################################################################
# Eliminate Directory Conflicts for New and Renamed Files
# 

any_dir_conflicts=

dir_file_conflicts_heading()
{
  if test ! -z "$report" ; then
    larch heading --sub "check for conflicts with new or renamed files\\n"
  fi
  any_dir_conflicts=yes
}

cd "$tree"

for file in `cat "$install_files_hard_conflicts"` ; do

  if test -h "$file" -o -e "$file" ; then
    deferred_conflict "$file"
    dir_file_conflicts_heading
    if test ! -z "$report" ; then
      printf "%s\\n" "$file" | larch body-indent --sub --nonl
    fi
  fi

done

for dir in `cat "$install_files_soft_dir_conflicts"` ; do

  if test "(" -h "$dir" -o -e "$dir" ")" -a ! -d "$dir" ; then
    deferred_conflict "$dir"
    dir_file_conflicts_heading
    if test ! -z "$report" ; then
      printf "%s\\n" "$file" | larch body-indent --sub --nonl
    fi
    any_dir_conflicts=yes
  fi

done

for dir in `cat $install_files_hard_conflicts | xargs -n 1 need-args dirname` ; do
  mkdir -p "$tree/$dir"
done

if test ! -z "$report" -a ! -z "$any_dir_conflicts" ; then
  printf "\\n"
fi


################################################################
# Put Renamed Files in New Locations
# 


if test ! -z "$report" -a ! -z "`head -n 1 \"$renamed_files_script\"`" ; then
  larch heading --sub "install renamed files\\n"
  cat "$renamed_files_script" | $ARCH_SHELL | larch body-indent --sub
else
  cat "$renamed_files_script" | $ARCH_SHELL > /dev/null
fi


################################################################
# Add New Files
# 

if test ! -z "$report" -a ! -z "`head -n 1 \"$needed_new_files_script\"`" ; then
  larch heading --sub "add new files\\n"
  cat "$needed_new_files_script" | $ARCH_SHELL | larch body-indent --sub
else
  cat "$needed_new_files_script" | $ARCH_SHELL > /dev/null
fi



################################################################
# Apply Regular File Patches
# 

has_body=
apply_patch_msg()
{
  if test ! -z "$report" ; then
    if test -z "$has_body" ; then
      has_body=yes
      larch heading --sub "apply patches\\n"
    fi
    printf "%s %s\\n" "$1" "$2" | larch body-indent --sub --nonl
  fi
}

apply_patch_msg_finish()
{
  if test ! -z "$report" -a ! -z "$has_body" ; then
    printf "\\n"
  fi
}

OLD_IFS="$IFS"
IFS="
"
for m in `cat "$patch_map"` ; do

  IFS="$OLD_IFS"

  tree_file="${m%% *}"
  mod_file="${m#* }"

  tree_file_base="`basename \"$tree_file\"`"
  tree_file_dir="`dirname \"$tree_file\"`"

  cd "$tree"
  cd "$tree_file_dir"

  preserve_old_patch_spew "$tree_file"

  if test -h "$tree_file_base" ; then

    made_rej_files=yes
    mv "$tree_file_base" "$tree_file_base.orig"
    cp "$delta/patches/$mod_file.patch" "$tree_file_base.rej"
    apply_patch_msg "C " "$tree_file"

  elif test ! -e "$tree_file_base" ; then

    # this really shouldn't ever be reached!
    #

    made_rej_files=yes
    touch "$tree_file_base.orig"
    chmod ugo-rwx "$tree_file_base.orig"
    cp "$delta/patches/$mod_file.patch" "$tree_file_base.rej"
    apply_patch_msg "??" "$tree_file"
    
  else

    is_automated_changelog=

    case "$tree_file_base" in

      ChangeLog|ChangeLog.*)	#
				# Is the tree file an automated ChangeLog for which the
				# tree has a patch log?
				# 
				magic="`head -1 "$tree_file_base" | grep \"automatically generated by arch changelog\" | cat`"

				if test ! -z "$magic" ; then

				  cl_version="`head -5 \"$tree_file_base\" | grep \"^# tag: automatic-ChangeLog--\" | head -1 | sed -e \"s/^# tag: automatic-ChangeLog--//\"`"

				  if ! test ! -z "$cl_version" && larch wd-check-for-patch-log "$cl_version" 2> /dev/null ; then
				    magic=
				  fi

				fi

				if test ! -z "$magic" ; then
				  is_automated_changelog=yes
				fi
				;;

      *)			is_automated_changelog=
      				;;

    esac

    if test ! -z "$is_automated_changelog" ; then
      # 
      # All automated changelogs are updated later...
      # 
      continue
    fi


    # 
    # TODO: insert long humorous flame about how GNU patch doesn't 
    #       come close to a reasonable interpretation of the Posix
    #	    standard, even with POSIXLY_CORRECT and how there doesn't
    # 	    seem to be a free replacement that does better.  Problem:
    # 	    it wouldn't be all that funny.
    # 
    # 	    Alternative: write a new version of patch just for arch.
    # 

    # CRITICAL: dopatch must *not* modify the original file in place.  
    # It *must* create a new output file.  This is in case the original
    # file is a hard link to a file we don't want to modify.
    # 
    mv "$tree_file_base" ",,dopatch.$$.$tree_file_base"
    set +e
    rm -f "$tmpdir/,,patch-output"
    patch $forward -f -s --posix -i "$delta/patches/$mod_file.patch" -o "$tree_file_base" ",,dopatch.$$.$tree_file_base" > "$tmpdir/,,patch-output" 2>&1 < /dev/null
    patchstat=$?
    set -e

    if test $patchstat -eq 0 ; then

      set-file-metadata `file-metadata --symlink --permissions ",,dopatch.$$.$tree_file_base"` "$tree_file_base"
      rm ",,dopatch.$$.$tree_file_base"
      apply_patch_msg "M " "$tree_file"

    elif test $patchstat -eq 1 ; then

      made_rej_files=yes
      set-file-metadata `file-metadata --symlink --permissions ",,dopatch.$$.$tree_file_base"` "$tree_file_base"
      mv ",,dopatch.$$.$tree_file_base" "$tree_file_base.orig"
      apply_patch_msg "C " "$tree_file"
      if test ! -z "$verbose" ; then
        cat "$tmpdir/,,patch-output" | larch body-indent --sub --nonl
      fi

    else

      printf "dopatch: internal error (patch)\\n" 1>&2
      printf "  patch exited with status > 1\\n" 1>&2
      printf "  command: %s" "patch -f -s --posix -c -i \"$delta/patches/$mod_file.patch\" -o \"$tree_file_base\" \",,dopatch.$$.$tree_file_base\"" 1>&2
      printf "\\n" 1>&2
      cat "$tmpdir/,,patch-output" 1>&2
      printf "\\n" 1>&2
      exit 2

    fi
  fi

done
IFS="$OLD_IFS"


# 
# Fix up all automated ChangeLogs -- even those not in the 
# patch.
# 
for log in `cut -s -f 1 "$final_tree_file_index" | grep -E -e "^.*(/ChangeLog|/ChangeLog\\..*)$" | cat` ; do

  #
  # Is the tree file an automated ChangeLog for which the
  # tree has a patch log?
  # 
  magic="`head -1 "$tree/$log" | grep \"automatically generated by arch changelog\" | cat`"

  if test ! -z "$magic" ; then

    cl_version="`head -5 \"$tree/$log\" \
		 | grep -E \"^(# line of development: |# (non-)?tag: automatic-ChangeLog--)\" \
		 | head -1 \
		 | sed -e \"s/^# tag: automatic-ChangeLog--//\" \
		       -e \"s/^# non-tag: automatic-ChangeLog--//\" \
		       -e \"s/^# line of development:[[:space:]]*//\"`"

    if ! test ! -z "$cl_version" && larch wd-check-for-patch-log "$cl_version" "$tree" 2> /dev/null ; then
      magic=
    fi

  fi

  if test ! -z "$magic" ; then
    apply_patch_msg "Ch" "$log"
    perms="`file-metadata --symlink --permissions \"$tree/$log\"`"

    # CRITICAL: the output file must not be modified in place since it  
    # may be a hard link to a file we don't want to modify.  I don't
    # think there is anything that prevents shell redirction (>) from
    # opening an existing file with O_TRUNC.
    # 
    if head -5 "$tree/$log" | grep -E -q "^(# non-tag: automatic-ChangeLog--|# line of development: )" ; then
      rm "$tree/$log"
      larch changelog --untagged --dir "$tree" "$cl_version" > "$tree/$log"
    else
      rm "$tree/$log"
      larch changelog --dir "$tree" "$cl_version" > "$tree/$log"
    fi
    
    set-file-metadata $perms "$tree/$log"
  fi

done



################################################################
# Patch Symbolic Links
# 

cd "$tree"

OLD_IFS="$IFS"
IFS="
"
for m in `cat "$link_patch_map"` ; do

  IFS="$OLD_IFS"

  tree_file="${m%% *}"
  mod_file="${m#* }"

  preserve_old_patch_spew "$tree_file"

  new_target="`cat \"$delta/patches/$mod_file.link-mod\"`"

  if test ! -h "$tree_file" -a ! -e "$tree_file" ; then

    # this shouldn't really be reached
    # 

    made_rej_files=yes
    touch "$tree_file.orig"
    chmod ugo-rwx "$tree_file.orig"
    ln -s "$new_target" "$tree_file.rej"
    apply_patch_msg "??" "$tree_file"


  elif test -e "$delta/patches/$mod_file.link-orig" ; then

    # patch specifies retargetting a symbolic link
    #

    orig_target="`cat \"$delta/patches/$mod_file.link-orig\"`"

    if test -e "$delta/patches/$mod_file.meta-orig" ; then
      orig_metadata="`cat \"$delta/patches/$mod_file.meta-orig\"`"
    else
      orig_metadata=
    fi

    made_link_rej=

    if test ! -h "$tree_file" ; then

      made_rej_files=yes
      made_link_rej=yes
      mv "$tree_file" "$tree_file.orig"
      ln -s "$new_target" "$tree_file.rej"
      set-file-metadata `file-metadata --symlink --permissions "$tree_file.orig"` "$tree_file.rej"
      apply_patch_msg "C>" "$tree_file"

    else      

      old_target_tree="`read-link \"$tree_file\"`"
      
      if test "$old_target_tree" = "$orig_target" ; then

        metadata="`file-metadata --symlink --permissions \"$tree_file\"`"
        rm "$tree_file"
	ln -s "$new_target" "$tree_file"
	set-file-metadata $metadata "$tree_file"
        apply_patch_msg "->" "$tree_file"

      else

        made_rej_files=yes
        made_link_rej=yes
        mv "$tree_file" "$tree_file.orig"
	ln -s "$new_target" "$tree_file.rej"
        set-file-metadata `file-metadata --symlink --permissions "$tree_file.orig"` "$tree_file.rej"
        apply_patch_msg "C>" "$tree_file"

      fi

      if test ! -z "$made_link_rej" ; then 
        ln -s "$orig_target" "$tree_file.patch-orig"
	if test ! -z "$orig_metadata" ; then
	  set-file-metadata $orig_metadata "$tree_file.patch-orig"
	fi
      fi

    fi

  else
    # patch must contain a .original file
    # 

    if cmp "$tree_file" "$delta/patches/$mod_file.original" ; then

      apply_patch_msg "->" "$tree_file"
      metadata="`file-metadata --symlink --permissions \"$tree_file\"`"
      rm "$tree_file"
      ln -s "$new_target" "$tree_file"
      set-file-metadata $metadata "$tree_file"

    else

      apply_patch_msg "C>" "$tree_file"
      made_rej_files=yes
      mv "$tree_file" "$tree_file.orig"
      ln -s "$new_target" "$tree_file.rej"
      set-file-metadata `file-metadata --symlink --permissions "$tree_file.orig"` "$tree_file.rej"
      cp -p "$delta/patches/$mod_file.original" "$tree_file.patch-orig"

    fi

  fi

done
IFS="$OLD_IFS"

################################################################
# Patch Binary Files and Symlinks Replaced by Regular Files 
# 

cd "$tree"

OLD_IFS="$IFS"
IFS="
"
for m in `cat "$binary_patch_map"` ; do

  IFS="$OLD_IFS"

  tree_file="${m%% *}"
  mod_file="${m#* }"

  preserve_old_patch_spew "$tree_file"

  if test ! -h "$tree_file" -a ! -e "$tree_file" ; then

    # shouldn't actually be reached
    # 
    made_rej_files=yes
    touch "$tree_file.orig"
    chmod ugo-rwx "$tree_file.orig"
    cp -p "$delta/patches/$mod_file.modified" "$tree_file.rej"
    apply_patch_msg "??" "$tree_file" 


  elif test -e "$delta/patches/$mod_file.link-orig" ; then

    # patch specifies replacing a symbolic link
    #

    orig_target="`cat \"$delta/patches/$mod_file.link-orig\"`"

    if test -e "$delta/patches/$mod_file.meta-orig" ; then
      orig_metadata="`cat \"$delta/patches/$mod_file.meta-orig\"`"
    else
      orig_metadata=
    fi

    made_bin_rej=

    if test ! -h "$tree_file" ; then

      made_rej_files=yes
      made_bin_rej=yes
      mv "$tree_file" "$tree_file.orig"
      cp "$delta/patches/$mod_file.modified" "$tree_file.rej"
      set-file-metadata `file-metadata --symlink --permissions "$tree_file.orig"` "$tree_file.rej"
      apply_patch_msg "CB" "$tree_file" 

    else      

      old_target_patch="`read-link \"$tree_file\"`"
      old_target_source="`cat \"$delta/patches/$mod_file.link-orig\"`"
      
      if test "$old_target_patch" = "$old_target_source" ; then

        metadata="`file-metadata --symlink --permissions \"$tree_file\"`"
        rm "$tree_file"
	cp "$delta/patches/$mod_file.modified" "$tree_file"
	set-file-metadata $metadata "$tree_file"
        apply_patch_msg "B " "$tree_file" 

      else

        made_rej_files=yes
	made_bin_rej=yes
        mv "$tree_file" "$tree_file.orig"
	cp "$delta/patches/$mod_file.modified" "$tree_file.rej"
	set-file-metadata `file-metadata --symlink --permissions "$tree_file.orig"` "$tree_file.rej"
        apply_patch_msg "CB" "$tree_file" 
      fi

      if test ! -z "$made_bin_rej" ; then 
        ln -s "$orig_target" "$tree_file.patch-orig"
	if test ! -z "$orig_metadata" ; then
	  set-file-metadata $orig_metadata "$tree_file.patch-orig"
	fi
      fi

    fi

  else
    # patch must contain a .original file
    # 

    if cmp "$tree_file" "$delta/patches/$mod_file.original" ; then

      metadata="`file-metadata --symlink --permissions \"$tree_file\"`"
      rm "$tree_file"
      cp "$delta/patches/$mod_file.modified" "$tree_file"
      set-file-metadata $metadata "$tree_file"
      apply_patch_msg "B " "$tree_file" 

    else

      made_rej_files=yes
      mv "$tree_file" "$mod_file.orig"
      cp "$delta/patches/$mod_file.modified" "$tree_file.rej"
      set-file-metadata `file-metadata --symlink --permissions "$tree_file.orig"` "$tree_file.rej"
      cp -p "$delta/patches/$mod_file.original" "$tree_file.patch-orig"
      apply_patch_msg "CB" "$tree_file" 
    fi

  fi

done
IFS="$OLD_IFS"

################################################################
# Patch File Metadata
# 

OLD_IFS="$IFS"
IFS="
"
for m in `cat "$metadata_patch_map"` ; do

  IFS="$OLD_IFS"

  tree_file="${m%% *}"
  mod_file="${m#* }"

  orig_perms="`cat \"$delta/patches/$mod_file.meta-orig\"`"
  mod_perms="`cat \"$delta/patches/$mod_file.meta-mod\"`"

  file_to_patch=

  if test -h "$tree/$tree_file" -o -e "$tree/$tree_file" ; then
    if test ! -h "$tree/$tree_file" ; then
      # CRITICAL: don't modify the meta-data of a file that might
      # be a hard-link to a file we don't want to modify.
      #
      rm -f ,,tmp 2> /dev/null
      find "$tree/$tree_file" -links +2 \
      | awk '{
	       print "mv \"" $1 "\" ,,tmp";
	       print "cp ,,tmp \"" $1 "\"";
	       print "rm -f ,,tmp";
   	     }' \
      | $ARCH_SHELL -e
    fi
    file_to_patch="$tree/$tree_file"
  elif test ! -e "$delta/patches/$mod_file.patch" ; then
    file_to_patch="$tree/$tree_file.rej"
  fi

  if test ! -z "$file_to_patch" ; then

    has_perms="`file-metadata --symlink --permissions \"$tree/$tree_file\"`"

    if test "$orig_perms" = "$has_perms" ; then
      set-file-metadata $mod_perms "$file_to_patch"
      has_perms="$mod_perms"
    fi

    if test "$has_perms" != "$mod_perms" ; then
      made_rej_files=yes
      cp "$delta/patches/$mod_file.meta-mod" "$tree/$tree_file.meta.rej"
      apply_patch_msg "C-" "$tree_file"
    else
      apply_patch_msg "--" "$tree_file"
    fi

  fi

done
IFS="$OLD_IFS"

################################################################
# Patch Dir Metadata
# 

OLD_IFS="$IFS"
IFS="
"
for m in `cat "$dir_metadata_patch_map"` ; do

  IFS="$OLD_IFS"

  tree_file="${m%% *}"
  mod_file="${m#* }"

  orig_perms="`cat \"$delta/patches/$mod_file.meta-orig\"`"
  mod_perms="`cat \"$delta/patches/$mod_file.meta-mod\"`"
  has_perms="`file-metadata --permissions \"$tree/$tree_file\"`"

  if test "$orig_perms" = "$has_perms" ; then
    set-file-metadata $mod_perms "$tree/$tree_file"
    has_perms="$mod_perms"
  fi

  if test "$has_perms" != "$mod_perms" ; then
    made_rej_files=yes
    cp "$delta/patches/$mod_file.meta-mod" "$tree/$tree_file/=dir-meta.rej"
    apply_patch_msg "c-" "$tree_file"
  else
    apply_patch_msg "d-" "$tree_file"
  fi

done
IFS="$OLD_IFS"

################################################################
# Finish Up List of Patched Files
# 

apply_patch_msg_finish


################################################################
# Put Patches for Missing Files in Tree
# 

if test ! -z "`head -n 1 \"$missing_file_patch_list\"`" ; then

  if test ! -z "$report" ; then
    larch heading --sub "copying patches for missing files to tree\\n"
  fi

  missing_file_patches="$tree/==missing-file-patches-`basename \"$patchdir\"`--`date +%Y%m%d.%H:%M:%S`"
  mkdir "$missing_file_patches"

  cat "$missing_file_patch_list" \
  | copy-file-list -- - "$delta/patches" "$missing_file_patches"

fi


################################################################
# Some Conflict Files Still Need to be Renamed to .rej Files
# 

if test ! -z "report" -a -e "$tmpdir/,,deferred_conflicts" ; then
  larch heading --sub "renaming conflicting dirs to .rej files\\n"
fi

cd "$tree"

if test -e "$tmpdir/,,deferred_conflicts" ; then

  for f in `cat "$tmpdir/,,deferred_conflicts" | sort -r` ; do
    mv "$f" "$f.rej"
    if test ! -z "$report" ; then
      printf "%s\\n" "$f" | larch body-indent --sub --nonl
    fi
  done

  if test ! -z "$report" ; then
    printf "\\n"
  fi

fi


################################################################
# Do We Really Need the Removed Files Archive? 
# 

if test -z "$delete_removed" ; then
  set +e
  rmdir "$archive" 2> /dev/null
  set -e
fi

################################################################
# Tell the User if Conflicts Occurred
# 

status=0

if test ! -z "$quiet" ; then
  larch heading --sub "patch report\\n"
fi


if test ! -z "$made_rej_files" ; then
  if test ! -z "$quiet" ; then
    larch heading --sub --sub "patch conflicts occured\\n"
    printf "look for .rej files\\n" | larch body-indent --sub --sub
  fi
  status=1
fi

if test -e "$missing_file_patches" ; then
  if test ! -z "$quiet" ; then
    larch heading --sub --sub  "patch contained modifications for missing files\\n" 
    printf "examine %s\\n" "$missing_file_patches" | larch body-indent --sub --sub
  fi
  status=1
fi

missing_renamed_files_log="$tree/++missing-renamed-files-`basename \"$patchdir\"`--`date +%Y%m%d.%H:%M:%S`"

if test ! -z "`head -n 1 \"$missing_renamed_files_index\"`" ; then

  if test ! -z "$quiet" ; then
    larch heading --sub --sub "patch wanted to rename some missing files\\n"
  fi

  saved_log=
  if cp "$missing_renamed_files_index" "$missing_renamed_files_log" ; then
    if test ! -z "$quiet" ; then
      larch heading --sub --sub --sub "log file: %s\\n" "$missing_renamed_files_log"
    fi
    saved_log=yes
  fi

  if test -z "$saved_log" -o ! -z "$report" ; then
    larch heading --sub --sub --sub "file list\\n"
    cut -s -d ' ' -f 1 "$missing_renamed_files_log" | larch body-indent --sub --sub --sub
  fi
  status=1
fi

missing_renamed_dirs_log="$tree/++missing-renamed-dirs-`basename \"$patchdir\"`--`date +%Y%m%d.%H:%M:%S`"

if test ! -z "`head -n 1 \"$missing_renamed_dirs_index\"`" ; then

  if test ! -z "$quiet" ; then
    larch heading --sub --sub "patch wanted to rename some missing dirs\\n" 1>&2
  fi

  saved_log=
  if cp "$missing_renamed_dirs_index" "$missing_renamed_dirs_log" ; then
    if test ! -z "$quiet" ; then
      larch heading --sub --sub --sub  "log file: %s\\n" "$missing_renamed_dirs_log"
    fi
    saved_log=yes
  fi

  if test -z "$saved_log" -o ! -z "$report" ; then
    larch heading --sub --sub "file list\\n"
    cat "$missing_renamed_dirs_log" | larch body-indent --sub --sub --sub
  fi
  status=1
fi

if test ! -z "$quiet" -a  $status -eq 0 ; then
  larch heading --sub --sub "patch cleanly applied\\n"
fi

if test -z "$debug" ; then
 cd "$tree"
 rm -rf "$tmpdir"
fi


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


exit "$status"

