#!/bin/sh
# 
# changelog.sh: generate a change log from a patch log
################################################################
# 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
# 
if test $# -ne 0 ; then

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

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


      --help|-h)
                printf "generate a change log from a patch log\\n"
                printf "usage: changelog [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 " -D --dir DIR                  use project tree containing DIR\\n"
                printf " --no-files                    exclude file lists from ChangeLog\\n"
                printf "\\n"
                printf " --new-entry PATCH,FILE        make FILE the first (top) entry\\n"
                printf "                                for patch level PATCH\\n"
                printf "\\n"
                printf " --untagged                    don't implicitly tag the output file\\n"
                printf "\\n"
                printf "Generate a ChangeLog for VERSION from the patch log for DIR.\\n"
                printf "\\n"
                exit 0
                ;;

      *)
                ;;
    esac
  done
fi

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

archroot=
archive=
dir=.
no_files=
new_entry=
untagged=

while test $# -ne 0 ; do

  case "$1" in 

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


    --untagged)         shift
                        untagged=--untagged
                        ;;

    --new-entry)        shift
                        if test $# -eq 0 ; then
                          printf "changelog: --new-entry requires an argument\\n" 1>&2
                          printf "try --help\\n" 1>&2
                          exit 1
                        fi
                        new_entry="$1"
                        shift
                        ;;

    --no-files)         shift
                        no_files=--no-files
                        ;;

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

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

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

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

    *)                  break
                        ;;
  esac

done



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

if test $# -gt 1 ; then
  printf "usage: changelog [options] [[archive]/revision]\\n" 1>&2
  printf "try --help\\n" 1>&2
  exit 1
fi

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

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

here="`pwd`"

if test ! -z "$new_entry" ; then
  new_entry_patch="${new_entry%%,*}"
  new_entry="${new_entry#*,}"
  cd "`dirname \"$new_entry\"`"
  new_entry="`pwd`/`basename \"$new_entry\"`"
fi

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

cd "$wdroot"

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

larch valid-package-name -e changelog --vsn "$version"

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

################################################################
# How to Translate One Log Entry to a ChangeLog Entry
# 
# 

entry()
{
  awk -v archive="$archive" \
      -v version="$version" \
      -v category="$category" \
      -v branch="$branch" \
      -v patch="$patch" \
      -v no_files="$no_files" \
    'BEGIN { getline;
             while (!match ($0, "^$"))
               {
                 field = tolower ($1);
                 sub (":.*", "", field);
                 headers[field] = $0;
                 sub ("^[^:]*:[ \t]*", "", headers[field]);
                 getline;
                 while (match ($0, "^[ \t]"))
                   {
                     headers[field] = headers[field] $0;
                     getline;
                   }
               }
           }

     { if (body == "") body = "    " $0; else body = body "\n    " $0; }
                  
     END {
           date = headers["standard-date"];

           if (date == "")
             {
               # this is almost vestigial: a backwards compatibility
               # hack just for the author of arch, some of whose log
               # messages pre-date the "standard-date" header field.
               # 
               split (headers["date"], ugly_date, "[ \t]+");
               date = ugly_date[6] "-" ugly_date[2] "-" ugly_date[3];
             }
           sub("[[:space:]].*GMT.*", " GMT", date);
           print date "\t" headers["creator"] "\t" patch;
           print "";
           print "    Summary:";
           print "      " headers["summary"];
           print "    Revision:";
           print "      " headers["revision"];
           print "";
           print body;
           print "";
           if (no_files == "")
             {
               file_list(0, "new-files", "{arch}/" category "/" branch "/" version "/" archive "/patch-log/" patch);
               file_list(0, "removed-files");
               file_list(0, "modified-files");
               file_pair_list("renamed-files");
               file_list(0, "new-directories");
               file_list(0, "removed-directories");
               file_list(0, "modified-directories");
               file_pair_list("renamed-directories");
               file_list(0, "new-patches", archive "/" version "--" patch);
               print ""
             }
         }

     function file_list (base_only, field_name, exclude)
     {
       for (x in items)
         delete items[x];

       n_items = split (headers[field_name], items, "[ \t]+");

       if ((n_items == 0) || ((exclude != "") && (n_items == 1)))
         return;

       sub("-", " ", field_name);
       print "    " field_name ":"
       printf("    ");
       width = 0;
       items_on_line = 0;

       for (x = 1; x <= n_items; ++x)
         {
           if (exclude == items[x])
             continue;
           if (base_only)
             sub (".*/", "", items[x]);
           if ((items_on_line == 0) || ((width + length (items[x]) + 1) < 64))
             {
               width += length (items[x]) + 1;
               ++items_on_line;
               printf(" %s", items[x]);
             }
           else
             {
               printf("\n");
               printf("    ");
               printf(" %s", items[x]);
               width = length(items[x]) + 1;
               items_on_line = 1;
             }
         }
       printf "\n"
       printf "\n"
     }
    function file_pair_list (field_name)
     {
       for (x in items)
         delete items[x];

       n_items = split (headers[field_name], items, "[ \t]+");

       if (n_items == 0)
         return;

       sub("-", " ", field_name);
       print "    " field_name ":"

       for (x = 1; x <= n_items; ++x)
         {
           printf("     %s\n", items[x]);
           printf("       ==> %s\n", items[x + 1]);
           x = x + 1;
         }
       printf "\n"
       printf "\n"
     }'
}

################################################################
# Print the ChangeLog
# 

printf "# do not edit -- automatically generated by arch changelog\\n"
if test -z "$untagged" ; then
  printf "# tag: automatic-ChangeLog--%s/%s\\n" "$archive" "$version"
else
  printf "# non-tag: automatic-ChangeLog--%s/%s\\n" "$archive" "$version"
fi
printf "#\\n"
printf "\\n"


if test ! -z "$new_entry" ; then
  patch="$new_entry_patch"
  cat "$new_entry" | entry
fi

cd "$wdroot/{arch}/$category/$branch/$version/$archive/patch-log"

for patch in `larch log-ls -r -A "$archive" "$version"` ; do

  cat "$patch" | entry

done



# tag: Tom Lord Thu Dec 13 04:46:59 2001 (patch-logs/changelog)
#
