# 
# whats-missing.sh - print patches missing from a project tree
################################################################
# Copyright (C) 2001, 2002 Tom Lord
# 
# See the file "COPYING" for further information about
# the copyright and warranty status of this work.
# 

set -e 

################################################################
# special options
# 
# Some options are special:
# 
#       --version | -V
#       --help | -h
#       --debug
# 
debug=

if test $# -ne 0 ; then

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

      --debug)  set -x
                debug=--debug
                shift
                ;;

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


      --help|-h)
                printf "print patches missing from a project tree\\n"
                printf "usage: whats-missing [options] [[archive/]version ...]\\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 " -d --dir DIR                  specify project tree\\n"
                printf " -r --reverse                  sort from oldest to newest\\n"
                printf " -s --summary                  print a summary of each patch\\n"
                printf " -f --full                     print full names of patch levels\\n"
                printf " --merges                      print the merge list for each patch\\n"
                printf "                                (implies --full)\\n"
                printf "\\n"
                printf "Print a list of patches missing in the project tree containing\\n"
                printf "DIR (or the current directory) for VERSION (or the default version.\\n"
                printf "of the project tree).\\n"
                printf "\\n"
                printf "The flag --merges means, for each patch, to print the list of patches\\n"
                printf "included in the patch in two columns.  For example:\\n"
                printf "\\n"
                printf "        PATCH-A\\tPATCH-A\\n"
                printf "        PATCH-A\\tPATCH-B\\n"
                printf "        PATCH-A\\tPATCH-C\\n"
                printf "\\n"
                printf "means that PATCH-A includes the changes from PATCH-B and PATCH-C.\\n"
                printf "(Every patch includes at least itself.)\\n"
                printf "\\n"
                printf "\\n"
                printf "\\n"
                exit 0
                ;;

      *)
                ;;
    esac
  done
fi

################################################################
# Ordinary Options
# 
# 
archroot=
archive=
reverse=
summary=
full=
dir=
merges=

while test $# -ne 0 ; do

  case "$1" in 

    --silent)           shift   # ignored
                        ;;
    --quiet)            shift   # ignored
                        ;;
    --report)           shift   # ignored
                        ;;
    --verbose)          shift   # ignored
                        ;;


    -R|--root)          shift
                        if test $# -eq 0 ; then
                          printf "whats-missing: -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 "whats-missing: -A and --archive require an argument\\n" 1>&2
                          printf "try --help\\n" 1>&2
                          exit 2
                        fi
                        archive="$1"
                        shift
                        ;;


    -d|--dir)           shift
                        if test $# -eq 0 ; then
                          printf "whats-missing: -d and --dir require an argument\\n" 1>&2
                          printf "try --help\\n" 1>&2
                          exit 2
                        fi
                        dir="$1"
                        shift
                        ;;


    -r|--reverse)       shift
                        reverse=-r
                        ;;

    -s|--summary)       shift
                        summary=--summary
                        ;;

    -f|--full)          shift
                        full=--full
                        ;;

    --merges)           shift
                        merges=--merges
                        full=--full
                        ;;

    --)                 shift
                        break
                        ;;

    -*)                 printf "whats-missing: unrecognized option (%s)\\n" "$1" 1>&2
                        printf "try --help\\n" 1>&2
                        exit 2
                        ;;

    *)                  break
                        ;;

  esac

done



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

if test $# -lt 0 ; then
  printf "usage: whats-missing [options] [[archive/]version ...]\\n" 1>&2
  printf "try --help\\n" 1>&2
  printf "\\n" 1>&2
  exit 1
fi

if test $# -ne 0 ; then
  archive_version="$1"
  shift
else
  archive_version=
fi

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

if test -z "$dir" ; then
  dir="`pwd`"
else
  cd "$dir"
  dir="`pwd`"
fi

cd "$dir"
wdroot="`larch tree-root --accurate`"

if test -z "$archive_version" ; then
  cd "$wdroot"
  archive_version="`larch tree-version`"
fi

larch valid-package-name -e whats-missing --tolerant -- "$archive_version"

arch="`larch parse-package-name -R \"$archroot\" -A \"$archive\" --arch \"$archive_version\"`"
non_arch="`larch parse-package-name -R \"$archroot\" -A \"$archive\" --non-arch \"$archive_version\"`"
category="`larch parse-package-name --basename \"$archive_version\"`"
branch="`larch parse-package-name --package \"$archive_version\"`"


################################################################
# Ensure that We Have an Archive Connection 
# 

if test "$WITHARCHIVE" != "$arch" ; then

  exec larch with-archive -A "$arch"  \
    larch whats-missing $debug --dir "$wdroot" $reverse $summary $merges $full "$arch/$non_arch" "$@"

fi


################################################################
# Print the List
# 

if larch valid-package-name --vsn "$archive_version" ; then
  version="`larch parse-package-name -R \"$archroot\" -A \"$arch\" --package-version \"$archive_version\"`"
else
  version="`larch versions --reverse \"$arch/$branch\" | head -n 1`"
fi


cd "$wdroot"

if ! larch logs | grep -s -q -F "$arch/$version" ; then
  printf "\\n" 1>&2
  printf "whats-missing: project tree has no patch-log for version\\n" 1>&2
  printf "\\n" 1>&2
  printf "  project tree: %s\\n" "$wdroot" 1>&2
  printf "  version: %s/%s\\n" $arch $version 1>&2
  printf "\\n" 1>&2
  printf "consider \"larch join-branch --help\"\\n" 1>&2
  exit 1
fi


rm -f ,,whats-missing.$$.archive
rm -f ,,whats-missing.$$.wd

clean_up()
{
  cd "$wdroot"
  rm -f ,,whats-missing.$$.archive
  rm -f ,,whats-missing.$$.wd
}

finish()
{
  clean_up
  exit "$1"
}

trap "printf \"whats-missing: interrupted -- cleaning up\\n\" 1>&2 ; finish 2" INT

larch log-ls "$arch/$version" | sort >  ,,whats-missing.$$.wd
larch revisions "$arch/$version" | sort > ,,whats-missing.$$.archive

if test ! -z "$full" ; then
  prefix="$arch/$version--"
else
  prefix=
fi

comm -2 -3 ,,whats-missing.$$.archive ,,whats-missing.$$.wd \
| sort -t- -k 1,1${reverse#-} -k 2,2${reverse#-}n \
| awk -v prefix="$prefix" \
      -v version="$arch/$version" \
      -v summary="$summary" \
      -v merges="$merges" \
      '{
         if (merges != "")
           {
             command="larch cat-archive-log --headers " prefix $1 " | larch log-header-field --list new-patches ";

             while (1)
               {
                 status = (command | getline included);

                 if (status == 0)
                   {
                     break;
                   }
                 else if (status > 0)
                   {
                     print prefix $1 " " included;
                   }
                 else
                   {
                     printf("\\n") | "cat 1>&2";
                     printf("whats-missing: I/O error reading log file\\n") | "cat 1>&2";
                     printf("  revision: \\n", $1) | "cat 1>&2"
                     printf("\\n") | "cat 1>&2";
                     exit(1);
                   }
               }
           }
         else
           {
             print prefix $1;
           }
         if (summary != "")
           {
             if (0 > system("larch cat-archive-log  " version "--" $1 " | larch log-header-field summary | sed -e \"s/^/    /\""))
               {
                 exit(1);
               }
           }
       }' \
|| finish 1

################################################################
# Tail Recurse on Remaining Arguments
# 

if test $# -ne 0 ; then
  clean_up
  exec larch whats-missing $debug -A "$archive" --dir "$wdroot" $reverse $summary $merges $full "$@"
else
  finish 0
fi
