#!/bin/sh
# tla-changelogs-to-log -- Construct an arch patch-log from ChangeLog changes
# Usage: tla-changelogs-to-log [OPTION...]
#
#  Copyright (C) 2003, 2004  Miles Bader <miles@gnu.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Written by Miles Bader <miles@gnu.org>
# 
#--
#   -b, --body             Only generate the body of the log.
#
#   -h, --header=TEXT      Add TEXT and a blank line before the body of the log
#
#   -s, --summary=MSG      Use MSG for the log Summary: header.
#   -n, --no-file-names    Omit filenames when attempting to construct an
#                          automatic Summary: header.
#   -p, --auto-prefix=PFX  Add PFX as prefix to any automatically generated
#                          Summary: header (with no intervening puncuation).
#                          See below for what happens if --summary and
#                          --auto-prefix are specified simulataneously.
#
#   -C, --no-changes-ok    Generate a log even if there appear to be
#                          no changelog changes (by default, this situation
#                          results in an error).
#
#       --help             Display a help message and exit.
#       --version          Display a release identifier string and exit.
#
# tla-changelogs-to-log constructs an arch patch-log from the changes to
# ChangeLog files in the project tree, and outputs it to stdout.
#
# If the -s/--summary option is not used, it will also attempt to construct
# a Summary: header, but will only do so if the ChangeLog changes can be
# reduced to fit on one line; otherwise it will abort, and ask you to use
# the -s/--summary option.  In such a case, the -n/--no-file-names option
# might be useful to make an automatically generated summary line short
# enough to fit (whether omitting filenames makes the resulting summary too
# ambiguous or not depends on the particular case).
#
# If both --summary=MSG and --auto-prefix=PFX are specified, then if an
# automatically generated summary (including PFX) would fit, it is used,
# otherwise MSG is used instead.

# (---- beginning of hdr.shpp ----)
# hdr.shpp

me=`basename $0`

bindir='/usr/bin'
AWK='/usr/bin/nawk'; export AWK
TLA='tla'; export TLA
SED='/bin/sed'; export SED
UUIDGEN='uuidgen'; export UUIDGEN

# (---- TLA_TOOLS_VERSION defined from ,tla-tools-version ----)
TLA_TOOLS_VERSION='jgoerzen@complete.org--debian/tla-tools--debian--1.0--patch-18
'
# (---- end of TLA_TOOLS_VERSION defined from ,tla-tools-version ----)

TLA_TOOL_PFX="${bindir+$bindir/}"
export TLA_TOOL_PFX

TLA_ESCAPE='yes'

if test "$TLA_ESCAPE" = yes; then
  TLA_UNESCAPED_OPT='--unescaped'
else
  TLA_UNESCAPED_OPT=''
fi

# Some tools get completely confused in stupid ways by non-default
# settings of LANG (like gawk, which fucks up regexp character ranges).
LANG=C; export LANG

# (---- end of hdr.shpp ----)
# (---- beginning of cmd-line.shpp ----)
# cmd-line.shpp -- Command-line helper functions for shell scripts

script="$0"
case "$script" in
  */*) ;;
  *)   script="${TLA_TOOL_PFX}$script";;
esac

usage ()
{
  $SED -n -e '/^\([^#]\|#-* *$\)/{s@.*@Usage: '"$me"' [--help|--version]@p;q;}'	\
         -e '/^# *Usage:/,/^# *$/{s/^# //p;q;}'				\
     < "$script"
}

short_help ()
{
  $SED -n -e '/^\([^#]\|-*# *$\|# *Usage:\)/q'				\
	 -e '/^#!/d;s/^.*-- */# /;s/^#[ 	]*//p'			\
     < "$script" | fmt
}

help_body ()
{
  $SED -n '/^ *$/q;/^#-/,/^[^#]/s/^#\( \|$\)//p' < "$script"
}

help ()
{
  usage
  short_help
  echo ''
  help_body
}

version ()
{
  local no_nl_vers=`echo "$TLA_TOOLS_VERSION"`
  echo "$me (tla-tools) $no_nl_vers"
  $SED -n '/^[^#]/q;/^#-/q;s/^# *\(Written by\)/\
\1/p' < "$script"
  $SED -n '/^[^#]/q;/^#-/q;s/^# *\(Copyright\)/\
\1/p' < "$script"
}

unrec_opt ()
{
  echo 1>&2 "$me: unrecognized option "\`"$1'"
  echo 1>&2 "Try "\`"$me --help' for more information."
}

cmd_line_err ()
{
  usage 1>&2
  echo 1>&2 "Try "\`"$me --help' for more information."
}

long_opt_val ()
{
  echo "$1" | $SED 's/^[^=]*=//'
}

short_opt_val ()
{
  echo "$1" | $SED 's/^-.//'
}

# (---- end of cmd-line.shpp ----)

summary=''
header=''
body_only=no
no_changes_ok=no
auto_sum_no_file_names=no
auto_sum_prefix=''

# Parse command-line options
while :; do
  case "$1" in
    -b|--body)
      body_only=yes; shift;;
    -h|--header)
      header="$2"; shift 2;;
    -h*)
      header=`short_opt_val "$1"`; shift;;
    --header=*)
      header=`long_opt_val "$1"`; shift;;
    -s|--summary)
      summary="$2"; shift 2;;
    -s*)
      summary=`short_opt_val "$1"`; shift;;
    --summary=*)
      summary=`long_opt_val "$1"`; shift;;
    -n|--no-file-names)
      auto_sum_no_file_names=yes; shift;;
    -p|--prefix|--auto-prefix)
      auto_sum_prefix="$2"; shift 2;;
    -p*)
      auto_sum_prefix=`short_opt_val "$1"`; shift;;
    --prefix=*|--auto-prefix=*)
      auto_sum_prefix=`long_opt_val "$1"`; shift;;
    -C|--no-changes-ok)
      no_changes_ok=yes; shift;;
    --help|-h|-H|-[?])
      help; exit 0;;
    --version|-V)
      version; exit 0;;
    -[!-]?*)
      # split concatenated single-letter options apart
      FIRST="$1"; shift
      set -- `echo $FIRST | $SED 's/-\(.\)\(.*\)/-\1 -\2/'` "$@"
      ;;
    -*)
      unrec_opt "$1"; exit 1;;
    *)
      break;
  esac
done

test "$#" -eq 0 || { cmd_line_err; exit 1; }

tree_root=`$TLA tree-root 2>/dev/null` || { echo 1>&2 "$me: Not in an arch project tree"; exit 2; }
cd "$tree_root"

changes=",,commit-with-changelogs-changes.$$"
trap "rm -f '$changes'" 0 1 2 3 11 15

$TLA changes --diffs |
$AWK > "$changes" '
 BEGIN {
   # Line prefixes used for header/body lines from changelog (with any
   # original whitespace removed).  The following is a slight tweak
   # (three spaces instead of a tab before body lines) to the normal
   # changelog format to allow more room for the file prefixes we add.
   #
   hdr_pfx = ""
   body_pfx = "   "

   exit_status = 1
 }

 /^[+][+][+] [^ \t]*\/[Cc]hange[Ll]og([^a-zA-Z\/][^\/]*$|$)/ {
   in_cl = 1
   after_file = 0
   ignore_entry = 0
   hdr = 0
   prefix = $2
   sub (/[Cc]hange[Ll]og([^a-zA-Z\/][^\/]*$|$)/, "", prefix)
   sub (/^mod\//, "", prefix)
   next
 }

 in_cl && /^(---|@@) / { next }
 in_cl && /^[+ ][^ \t]/ { hdr = $0; ignore_entry = 0; blank = 1; after_file = 0; next }
 in_cl && !ignore_entry && /^[+ ][ \t]*$/ { blank = 1; after_file = 0; next }
 in_cl && !ignore_entry && /^[+]/ {
   sub (/^[+][ \t]*/, "")

   if (blank && prev) {
     print ""
     continuing_file_list = 0
   }

   if (hdr) {
     sub (/^[+ ]/, hdr_pfx, hdr)
     print hdr
     print ""
     hdr = 0
   }

   if ($1 == "*" || continuing_file_list) {
     filename_field = ($1 == "*" ? 2 : 1)
     while (filename_field <= NF) {
       if ($filename_field ~ /^[[(]/)
	 break;
       $filename_field = prefix $filename_field
       if ($filename_field ~ /:$/)
	 break;
       filename_field++
     }
     continuing_file_list = (filename_field > NF)
     file_line = (filename_field > 1)
     after_file = 1
   } else
     file_line = 0

   # Add a prefix to non-file lines
#   if (!file_line && after_file && !continuing_file_list)
#     $0 = "  " $0

   print body_pfx $0
   exit_status = 0

   prev = 1
   blank = 0
   next
 }
 in_cl && /^[ ]/ { ignore_entry = 1; next }
 in_cl { in_cl = 0 }

 END { exit (exit_status) }
' || test "$no_changes_ok" = yes || { echo 1>&2 "$me: No ChangeLog additions found"; exit 6; }

# Generate a summary from changelog entries in "$changes"
make_auto_summary ()
{
  # Sed command to scrunch down a normal changelog body line
  if test $auto_sum_no_file_names = yes; then
    # Omit filenames
    scrunch_cmd='s/^[\t ]*[*][\t ]*\([^(:]*:[\t ]*\|[^(]*\((\)\|\)/\2/'
  else
    # Include filenames
    scrunch_cmd='s/^[\t ]*[*][\t ]*//'
  fi

  echo -n "$auto_sum_prefix"

  $SED -e '/^$/d' -e '/^[^\t ]/d' -e "$scrunch_cmd" "$@" | fmt
}

# return a non-zero status if the summary in $1 is `too long' to fit on one line
bad_summary ()
{
  test `echo "$1" | wc -l` -ne 1 || echo "$1" | egrep '.{71}|^$' >/dev/null
}

if test "$body_only" != yes; then
  if test x"$summary" = x; then
    # No summary provided, try to automatically generate one from $changes

    summary=`make_auto_summary "$changes"`

    # See if it's too long or not, and if so, abort
    if bad_summary "$summary"; then
      echo 1>&2 "$me: Cannot generate automatic summary; please use --summary option"
      exit 7
    fi
  elif test x"$auto_sum_prefix" != x && test -s "$changes"; then
    # A summary was given, but the user _also_ specified a auto-summary
    # prefix; in this case what we use the prefix plus and automatic summary
    # if it fits, otherwise we use the explicit summary given with
    # -s/--summary.

    auto_summary=`make_auto_summary "$changes"`

    if ! bad_summary "$auto_summary"; then
      summary="$auto_summary"
    fi
  fi

  echo "Summary: $summary"
  echo ""
fi

test x"$header" = x || echo "$header"

if test -s "$changes"; then
  test x"$header" = x || echo ""
  cat "$changes"
fi

