11e72d8d2Sderaadt#! /bin/sh 21e72d8d2Sderaadt 31e72d8d2Sderaadt# RCS to ChangeLog generator 41e72d8d2Sderaadt 5c71bc7e2Stholo# Generate a change log prefix from RCS files (perhaps in the CVS repository) 6c71bc7e2Stholo# and the ChangeLog (if any). 71e72d8d2Sderaadt# Output the new prefix to standard output. 81e72d8d2Sderaadt# You can edit this prefix by hand, and then prepend it to ChangeLog. 91e72d8d2Sderaadt 101e72d8d2Sderaadt# Ignore log entries that start with `#'. 111e72d8d2Sderaadt# Clump together log entries that start with `{topic} ', 121e72d8d2Sderaadt# where `topic' contains neither white space nor `}'. 131e72d8d2Sderaadt 14c71bc7e2StholoHelp='The default FILEs are the files registered under the working directory. 15c71bc7e2StholoOptions: 161e72d8d2Sderaadt 17c71bc7e2Stholo -c CHANGELOG Output a change log prefix to CHANGELOG (default ChangeLog). 18c71bc7e2Stholo -h HOSTNAME Use HOSTNAME in change log entries (default current host). 19c71bc7e2Stholo -i INDENT Indent change log lines by INDENT spaces (default 8). 20c71bc7e2Stholo -l LENGTH Try to limit log lines to LENGTH characters (default 79). 21c71bc7e2Stholo -R If no FILEs are given and RCS is used, recurse through working directory. 22c71bc7e2Stholo -r OPTION Pass OPTION to subsidiary log command. 23c71bc7e2Stholo -t TABWIDTH Tab stops are every TABWIDTH characters (default 8). 24c71bc7e2Stholo -u "LOGIN<tab>FULLNAME<tab>MAILADDR" Assume LOGIN has FULLNAME and MAILADDR. 25c71bc7e2Stholo -v Append RCS revision to file names in log lines. 26c71bc7e2Stholo --help Output help. 27c71bc7e2Stholo --version Output version number. 28c71bc7e2Stholo 29c71bc7e2StholoReport bugs to <bug-gnu-emacs@gnu.org>.' 30c71bc7e2Stholo 31*68ccc4cdSmillertId='$Id: rcs2log.sh,v 1.2 2001/08/07 22:00:56 millert Exp $' 32c71bc7e2Stholo 33c71bc7e2Stholo# Copyright 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc. 341e72d8d2Sderaadt 351e72d8d2Sderaadt# This program is free software; you can redistribute it and/or modify 361e72d8d2Sderaadt# it under the terms of the GNU General Public License as published by 371e72d8d2Sderaadt# the Free Software Foundation; either version 2, or (at your option) 381e72d8d2Sderaadt# any later version. 391e72d8d2Sderaadt# 401e72d8d2Sderaadt# This program is distributed in the hope that it will be useful, 411e72d8d2Sderaadt# but WITHOUT ANY WARRANTY; without even the implied warranty of 421e72d8d2Sderaadt# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 431e72d8d2Sderaadt# GNU General Public License for more details. 441e72d8d2Sderaadt# 451e72d8d2Sderaadt# You should have received a copy of the GNU General Public License 4650bf276cStholo# along with this program; see the file COPYING. If not, write to the 4750bf276cStholo# Free Software Foundation, Inc., 59 Temple Place - Suite 330, 4850bf276cStholo# Boston, MA 02111-1307, USA. 491e72d8d2Sderaadt 50c71bc7e2StholoCopyright='Copyright 1998 Free Software Foundation, Inc. 51c71bc7e2StholoThis program comes with NO WARRANTY, to the extent permitted by law. 52c71bc7e2StholoYou may redistribute copies of this program 53c71bc7e2Stholounder the terms of the GNU General Public License. 54c71bc7e2StholoFor more information about these matters, see the files named COPYING. 55c71bc7e2StholoAuthor: Paul Eggert <eggert@twinsun.com>' 56c71bc7e2Stholo 571e72d8d2Sderaadttab=' ' 581e72d8d2Sderaadtnl=' 591e72d8d2Sderaadt' 601e72d8d2Sderaadt 611e72d8d2Sderaadt# Parse options. 621e72d8d2Sderaadt 631e72d8d2Sderaadt# defaults 641e72d8d2Sderaadt: ${AWK=awk} 651e72d8d2Sderaadt: ${TMPDIR=/tmp} 66c71bc7e2Stholochangelog=ChangeLog # change log file name 67c71bc7e2Stholodatearg= # rlog date option 681e72d8d2Sderaadthostname= # name of local host (if empty, will deduce it later) 691e72d8d2Sderaadtindent=8 # indent of log line 701e72d8d2Sderaadtlength=79 # suggested max width of log line 711e72d8d2Sderaadtlogins= # login names for people we know fullnames and mailaddrs of 721e72d8d2SderaadtloginFullnameMailaddrs= # login<tab>fullname<tab>mailaddr triplets 73c71bc7e2StholologTZ= # time zone for log dates (if empty, use local time) 741e72d8d2Sderaadtrecursive= # t if we want recursive rlog 75c71bc7e2Stholorevision= # t if we want revision numbers 761e72d8d2Sderaadtrlog_options= # options to pass to rlog 771e72d8d2Sderaadttabwidth=8 # width of horizontal tab 781e72d8d2Sderaadt 791e72d8d2Sderaadtwhile : 801e72d8d2Sderaadtdo 811e72d8d2Sderaadt case $1 in 82c71bc7e2Stholo -c) changelog=${2?}; shift;; 831e72d8d2Sderaadt -i) indent=${2?}; shift;; 841e72d8d2Sderaadt -h) hostname=${2?}; shift;; 851e72d8d2Sderaadt -l) length=${2?}; shift;; 861e72d8d2Sderaadt -[nu]) # -n is obsolescent; it is replaced by -u. 871e72d8d2Sderaadt case $1 in 881e72d8d2Sderaadt -n) case ${2?}${3?}${4?} in 891e72d8d2Sderaadt *"$tab"* | *"$nl"*) 901e72d8d2Sderaadt echo >&2 "$0: -n '$2' '$3' '$4': tabs, newlines not allowed" 911e72d8d2Sderaadt exit 1 921e72d8d2Sderaadt esac 93c71bc7e2Stholo case $loginFullnameMailaddrs in 94c71bc7e2Stholo '') loginFullnameMailaddrs=$2$tab$3$tab$4;; 95c71bc7e2Stholo ?*) loginFullnameMailaddrs=$loginFullnameMailaddrs$nl$2$tab$3$tab$4 96c71bc7e2Stholo esac 971e72d8d2Sderaadt shift; shift; shift;; 981e72d8d2Sderaadt -u) 991e72d8d2Sderaadt # If $2 is not tab-separated, use colon for separator. 1001e72d8d2Sderaadt case ${2?} in 1011e72d8d2Sderaadt *"$nl"*) 1021e72d8d2Sderaadt echo >&2 "$0: -u '$2': newlines not allowed" 1031e72d8d2Sderaadt exit 1;; 1041e72d8d2Sderaadt *"$tab"*) 1051e72d8d2Sderaadt t=$tab;; 1061e72d8d2Sderaadt *) 1071e72d8d2Sderaadt t=: 1081e72d8d2Sderaadt esac 1091e72d8d2Sderaadt case $2 in 1101e72d8d2Sderaadt *"$t"*"$t"*"$t"*) 1111e72d8d2Sderaadt echo >&2 "$0: -u '$2': too many fields" 1121e72d8d2Sderaadt exit 1;; 1131e72d8d2Sderaadt *"$t"*"$t"*) 1141e72d8d2Sderaadt ;; 1151e72d8d2Sderaadt *) 1161e72d8d2Sderaadt echo >&2 "$0: -u '$2': not enough fields" 1171e72d8d2Sderaadt exit 1 1181e72d8d2Sderaadt esac 119c71bc7e2Stholo case $loginFullnameMailaddrs in 120c71bc7e2Stholo '') loginFullnameMailaddrs=$2;; 121c71bc7e2Stholo ?*) loginFullnameMailaddrs=$loginFullnameMailaddrs$nl$2 122c71bc7e2Stholo esac 1231e72d8d2Sderaadt shift 1241e72d8d2Sderaadt esac 125c71bc7e2Stholo case $logins in 126c71bc7e2Stholo '') logins=$login;; 127c71bc7e2Stholo ?*) logins=$logins$nl$login 128c71bc7e2Stholo esac 1291e72d8d2Sderaadt ;; 130c71bc7e2Stholo -r) 131c71bc7e2Stholo case $rlog_options in 132c71bc7e2Stholo '') rlog_options=${2?};; 133c71bc7e2Stholo ?*) rlog_options=$rlog_options$nl${2?} 134c71bc7e2Stholo esac 135c71bc7e2Stholo shift;; 1361e72d8d2Sderaadt -R) recursive=t;; 1371e72d8d2Sderaadt -t) tabwidth=${2?}; shift;; 138c71bc7e2Stholo -v) revision=t;; 139c71bc7e2Stholo --version) 140c71bc7e2Stholo set $Id 141c71bc7e2Stholo rcs2logVersion=$3 142c71bc7e2Stholo echo >&2 "rcs2log (GNU Emacs) $rcs2logVersion$nl$Copyright" 143c71bc7e2Stholo exit 0;; 144c71bc7e2Stholo -*) echo >&2 "Usage: $0 [OPTION]... [FILE ...]$nl$Help" 145c71bc7e2Stholo case $1 in 146c71bc7e2Stholo --help) exit 0;; 147c71bc7e2Stholo *) exit 1 148c71bc7e2Stholo esac;; 1491e72d8d2Sderaadt *) break 1501e72d8d2Sderaadt esac 1511e72d8d2Sderaadt shift 1521e72d8d2Sderaadtdone 1531e72d8d2Sderaadt 1541e72d8d2Sderaadtmonth_data=' 1551e72d8d2Sderaadt m[0]="Jan"; m[1]="Feb"; m[2]="Mar" 1561e72d8d2Sderaadt m[3]="Apr"; m[4]="May"; m[5]="Jun" 1571e72d8d2Sderaadt m[6]="Jul"; m[7]="Aug"; m[8]="Sep" 1581e72d8d2Sderaadt m[9]="Oct"; m[10]="Nov"; m[11]="Dec" 1591e72d8d2Sderaadt' 1601e72d8d2Sderaadt 1611e72d8d2Sderaadt 1621e72d8d2Sderaadt# Put rlog output into $rlogout. 1631e72d8d2Sderaadt 1641e72d8d2Sderaadt# If no rlog options are given, 1651e72d8d2Sderaadt# log the revisions checked in since the first ChangeLog entry. 166c71bc7e2Stholo# Since ChangeLog is only by date, some of these revisions may be duplicates of 167c71bc7e2Stholo# what's already in ChangeLog; it's the user's responsibility to remove them. 1681e72d8d2Sderaadtcase $rlog_options in 1691e72d8d2Sderaadt'') 170c71bc7e2Stholo if test -s "$changelog" 1711e72d8d2Sderaadt then 1721e72d8d2Sderaadt e=' 173c71bc7e2Stholo /^[0-9]+-[0-9][0-9]-[0-9][0-9]/{ 174c71bc7e2Stholo # ISO 8601 date 175c71bc7e2Stholo print $1 176c71bc7e2Stholo exit 177c71bc7e2Stholo } 1781e72d8d2Sderaadt /^... ... [ 0-9][0-9] [ 0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9]+ /{ 179c71bc7e2Stholo # old-fashioned date and time (Emacs 19.31 and earlier) 1801e72d8d2Sderaadt '"$month_data"' 1811e72d8d2Sderaadt year = $5 1821e72d8d2Sderaadt for (i=0; i<=11; i++) if (m[i] == $2) break 1831e72d8d2Sderaadt dd = $3 184c71bc7e2Stholo printf "%d-%02d-%02d\n", year, i+1, dd 1851e72d8d2Sderaadt exit 1861e72d8d2Sderaadt } 1871e72d8d2Sderaadt ' 188c71bc7e2Stholo d=`$AWK "$e" <"$changelog"` || exit 1891e72d8d2Sderaadt case $d in 190c71bc7e2Stholo ?*) datearg="-d>$d" 1911e72d8d2Sderaadt esac 1921e72d8d2Sderaadt fi 1931e72d8d2Sderaadtesac 1941e72d8d2Sderaadt 195c71bc7e2Stholo# Use TZ specified by ChangeLog local variable, if any. 196c71bc7e2Stholoif test -s "$changelog" 197c71bc7e2Stholothen 198c71bc7e2Stholo extractTZ=' 199c71bc7e2Stholo /^.*change-log-time-zone-rule['"$tab"' ]*:['"$tab"' ]*"\([^"]*\)".*/{ 200c71bc7e2Stholo s//\1/; p; q 201c71bc7e2Stholo } 202c71bc7e2Stholo /^.*change-log-time-zone-rule['"$tab"' ]*:['"$tab"' ]*t.*/{ 203c71bc7e2Stholo s//UTC0/; p; q 204c71bc7e2Stholo } 205c71bc7e2Stholo ' 206c71bc7e2Stholo logTZ=`tail "$changelog" | sed -n "$extractTZ"` 207c71bc7e2Stholo case $logTZ in 208c71bc7e2Stholo ?*) TZ=$logTZ; export TZ 209c71bc7e2Stholo esac 210c71bc7e2Stholofi 211c71bc7e2Stholo 2121e72d8d2Sderaadt# If CVS is in use, examine its repository, not the normal RCS files. 2131e72d8d2Sderaadtif test ! -f CVS/Repository 2141e72d8d2Sderaadtthen 2151e72d8d2Sderaadt rlog=rlog 2161e72d8d2Sderaadt repository= 2171e72d8d2Sderaadtelse 218c71bc7e2Stholo rlog='cvs -q log' 2191e72d8d2Sderaadt repository=`sed 1q <CVS/Repository` || exit 2201e72d8d2Sderaadt test ! -f CVS/Root || CVSROOT=`cat <CVS/Root` || exit 2211e72d8d2Sderaadt case $CVSROOT in 2221e72d8d2Sderaadt *:/*) 2231e72d8d2Sderaadt # remote repository 2241e72d8d2Sderaadt ;; 2251e72d8d2Sderaadt *) 2261e72d8d2Sderaadt # local repository 2271e72d8d2Sderaadt case $repository in 2281e72d8d2Sderaadt /*) ;; 2291e72d8d2Sderaadt *) repository=${CVSROOT?}/$repository 2301e72d8d2Sderaadt esac 2311e72d8d2Sderaadt if test ! -d "$repository" 2321e72d8d2Sderaadt then 2331e72d8d2Sderaadt echo >&2 "$0: $repository: bad repository (see CVS/Repository)" 2341e72d8d2Sderaadt exit 1 2351e72d8d2Sderaadt fi 2361e72d8d2Sderaadt esac 2371e72d8d2Sderaadtfi 2381e72d8d2Sderaadt 239c71bc7e2Stholo# Use $rlog's -zLT option, if $rlog supports it. 240c71bc7e2Stholocase `$rlog -zLT 2>&1` in 241c71bc7e2Stholo*' option'*) ;; 242c71bc7e2Stholo*) 243c71bc7e2Stholo case $rlog_options in 244c71bc7e2Stholo '') rlog_options=-zLT;; 245c71bc7e2Stholo ?*) rlog_options=-zLT$nl$rlog_options 246c71bc7e2Stholo esac 247c71bc7e2Stholoesac 248c71bc7e2Stholo 2491e72d8d2Sderaadt# With no arguments, examine all files under the RCS directory. 2501e72d8d2Sderaadtcase $# in 2511e72d8d2Sderaadt0) 2521e72d8d2Sderaadt case $repository in 2531e72d8d2Sderaadt '') 2541e72d8d2Sderaadt oldIFS=$IFS 2551e72d8d2Sderaadt IFS=$nl 2561e72d8d2Sderaadt case $recursive in 2571e72d8d2Sderaadt t) 2581e72d8d2Sderaadt RCSdirs=`find . -name RCS -type d -print` 2591e72d8d2Sderaadt filesFromRCSfiles='s|,v$||; s|/RCS/|/|; s|^\./||' 2601e72d8d2Sderaadt files=` 2611e72d8d2Sderaadt { 2621e72d8d2Sderaadt case $RCSdirs in 263c71bc7e2Stholo ?*) find $RCSdirs \ 264c71bc7e2Stholo -type f \ 265c71bc7e2Stholo ! -name '*_' \ 266c71bc7e2Stholo ! -name ',*,' \ 267c71bc7e2Stholo ! -name '.*_' \ 268c71bc7e2Stholo ! -name .rcsfreeze.log \ 269c71bc7e2Stholo ! -name .rcsfreeze.ver \ 270c71bc7e2Stholo -print 2711e72d8d2Sderaadt esac 2721e72d8d2Sderaadt find . -name '*,v' -print 2731e72d8d2Sderaadt } | 2741e72d8d2Sderaadt sort -u | 2751e72d8d2Sderaadt sed "$filesFromRCSfiles" 2761e72d8d2Sderaadt `;; 2771e72d8d2Sderaadt *) 2781e72d8d2Sderaadt files= 2791e72d8d2Sderaadt for file in RCS/.* RCS/* .*,v *,v 2801e72d8d2Sderaadt do 2811e72d8d2Sderaadt case $file in 282c71bc7e2Stholo RCS/. | RCS/.. | RCS/,*, | RCS/*_) continue;; 283c71bc7e2Stholo RCS/.rcsfreeze.log | RCS/.rcsfreeze.ver) continue;; 284c71bc7e2Stholo RCS/.\* | RCS/\* | .\*,v | \*,v) test -f "$file" || continue;; 285c71bc7e2Stholo RCS/*,v | RCS/.*,v) ;; 286c71bc7e2Stholo RCS/* | RCS/.*) test -f "$file" || continue 2871e72d8d2Sderaadt esac 288c71bc7e2Stholo case $files in 289c71bc7e2Stholo '') files=$file;; 290c71bc7e2Stholo ?*) files=$files$nl$file 291c71bc7e2Stholo esac 2921e72d8d2Sderaadt done 2931e72d8d2Sderaadt case $files in 2941e72d8d2Sderaadt '') exit 0 2951e72d8d2Sderaadt esac 2961e72d8d2Sderaadt esac 2971e72d8d2Sderaadt set x $files 2981e72d8d2Sderaadt shift 2991e72d8d2Sderaadt IFS=$oldIFS 3001e72d8d2Sderaadt esac 3011e72d8d2Sderaadtesac 3021e72d8d2Sderaadt 303*68ccc4cdSmillertllogout=`mktemp $TMPDIR/rcs2log_l.XXXXXXXXXX` || exit 1 304*68ccc4cdSmillertrlogout=`mktemp $TMPDIR/rcs2log_r.XXXXXXXXXX` || { 305*68ccc4cdSmillert rm -f $llogout 306*68ccc4cdSmillert exit 1 307*68ccc4cdSmillert} 3081e72d8d2Sderaadttrap exit 1 2 13 15 3091e72d8d2Sderaadttrap "rm -f $llogout $rlogout; exit 1" 0 3101e72d8d2Sderaadt 311c71bc7e2Stholocase $datearg in 312c71bc7e2Stholo?*) $rlog $rlog_options "$datearg" ${1+"$@"} >$rlogout;; 313c71bc7e2Stholo'') $rlog $rlog_options ${1+"$@"} >$rlogout 3141e72d8d2Sderaadtesac || exit 3151e72d8d2Sderaadt 3161e72d8d2Sderaadt 3171e72d8d2Sderaadt# Get the full name of each author the logs mention, and set initialize_fullname 3181e72d8d2Sderaadt# to awk code that initializes the `fullname' awk associative array. 3191e72d8d2Sderaadt# Warning: foreign authors (i.e. not known in the passwd file) are mishandled; 3201e72d8d2Sderaadt# you have to fix the resulting output by hand. 3211e72d8d2Sderaadt 3221e72d8d2Sderaadtinitialize_fullname= 3231e72d8d2Sderaadtinitialize_mailaddr= 3241e72d8d2Sderaadt 3251e72d8d2Sderaadtcase $loginFullnameMailaddrs in 3261e72d8d2Sderaadt?*) 3271e72d8d2Sderaadt case $loginFullnameMailaddrs in 3281e72d8d2Sderaadt *\"* | *\\*) 3291e72d8d2Sderaadt sed 's/["\\]/\\&/g' >$llogout <<EOF || exit 3301e72d8d2Sderaadt$loginFullnameMailaddrs 3311e72d8d2SderaadtEOF 3321e72d8d2Sderaadt loginFullnameMailaddrs=`cat $llogout` 3331e72d8d2Sderaadt esac 3341e72d8d2Sderaadt 3351e72d8d2Sderaadt oldIFS=$IFS 3361e72d8d2Sderaadt IFS=$nl 3371e72d8d2Sderaadt for loginFullnameMailaddr in $loginFullnameMailaddrs 3381e72d8d2Sderaadt do 3391e72d8d2Sderaadt case $loginFullnameMailaddr in 3401e72d8d2Sderaadt *"$tab"*) IFS=$tab;; 3411e72d8d2Sderaadt *) IFS=: 3421e72d8d2Sderaadt esac 3431e72d8d2Sderaadt set x $loginFullnameMailaddr 3441e72d8d2Sderaadt login=$2 3451e72d8d2Sderaadt fullname=$3 3461e72d8d2Sderaadt mailaddr=$4 3471e72d8d2Sderaadt initialize_fullname="$initialize_fullname 3481e72d8d2Sderaadt fullname[\"$login\"] = \"$fullname\"" 3491e72d8d2Sderaadt initialize_mailaddr="$initialize_mailaddr 3501e72d8d2Sderaadt mailaddr[\"$login\"] = \"$mailaddr\"" 3511e72d8d2Sderaadt done 3521e72d8d2Sderaadt IFS=$oldIFS 3531e72d8d2Sderaadtesac 3541e72d8d2Sderaadt 3551e72d8d2Sderaadtcase $llogout in 3561e72d8d2Sderaadt?*) sort -u -o $llogout <<EOF || exit 3571e72d8d2Sderaadt$logins 3581e72d8d2SderaadtEOF 3591e72d8d2Sderaadtesac 3601e72d8d2Sderaadtoutput_authors='/^date: / { 3611e72d8d2Sderaadt if ($2 ~ /^[0-9]*[-\/][0-9][0-9][-\/][0-9][0-9]$/ && $3 ~ /^[0-9][0-9]:[0-9][0-9]:[0-9][0-9][-+0-9:]*;$/ && $4 == "author:" && $5 ~ /^[^;]*;$/) { 3621e72d8d2Sderaadt print substr($5, 1, length($5)-1) 3631e72d8d2Sderaadt } 3641e72d8d2Sderaadt}' 3651e72d8d2Sderaadtauthors=` 3661e72d8d2Sderaadt $AWK "$output_authors" <$rlogout | 3671e72d8d2Sderaadt case $llogout in 3681e72d8d2Sderaadt '') sort -u;; 3691e72d8d2Sderaadt ?*) sort -u | comm -23 - $llogout 3701e72d8d2Sderaadt esac 3711e72d8d2Sderaadt` 3721e72d8d2Sderaadtcase $authors in 3731e72d8d2Sderaadt?*) 3741e72d8d2Sderaadt cat >$llogout <<EOF || exit 3751e72d8d2Sderaadt$authors 3761e72d8d2SderaadtEOF 3771e72d8d2Sderaadt initialize_author_script='s/["\\]/\\&/g; s/.*/author[\"&\"] = 1/' 3781e72d8d2Sderaadt initialize_author=`sed -e "$initialize_author_script" <$llogout` 3791e72d8d2Sderaadt awkscript=' 3801e72d8d2Sderaadt BEGIN { 3811e72d8d2Sderaadt alphabet = "abcdefghijklmnopqrstuvwxyz" 3821e72d8d2Sderaadt ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 3831e72d8d2Sderaadt '"$initialize_author"' 3841e72d8d2Sderaadt } 3851e72d8d2Sderaadt { 3861e72d8d2Sderaadt if (author[$1]) { 3871e72d8d2Sderaadt fullname = $5 3881e72d8d2Sderaadt if (fullname ~ /[0-9]+-[^(]*\([0-9]+\)$/) { 3891e72d8d2Sderaadt # Remove the junk from fullnames like "0000-Admin(0000)". 3901e72d8d2Sderaadt fullname = substr(fullname, index(fullname, "-") + 1) 3911e72d8d2Sderaadt fullname = substr(fullname, 1, index(fullname, "(") - 1) 3921e72d8d2Sderaadt } 3931e72d8d2Sderaadt if (fullname ~ /,[^ ]/) { 3941e72d8d2Sderaadt # Some sites put comma-separated junk after the fullname. 3951e72d8d2Sderaadt # Remove it, but leave "Bill Gates, Jr" alone. 3961e72d8d2Sderaadt fullname = substr(fullname, 1, index(fullname, ",") - 1) 3971e72d8d2Sderaadt } 3981e72d8d2Sderaadt abbr = index(fullname, "&") 3991e72d8d2Sderaadt if (abbr) { 4001e72d8d2Sderaadt a = substr($1, 1, 1) 4011e72d8d2Sderaadt A = a 4021e72d8d2Sderaadt i = index(alphabet, a) 4031e72d8d2Sderaadt if (i) A = substr(ALPHABET, i, 1) 4041e72d8d2Sderaadt fullname = substr(fullname, 1, abbr-1) A substr($1, 2) substr(fullname, abbr+1) 4051e72d8d2Sderaadt } 4061e72d8d2Sderaadt 4071e72d8d2Sderaadt # Quote quotes and backslashes properly in full names. 4081e72d8d2Sderaadt # Do not use gsub; traditional awk lacks it. 4091e72d8d2Sderaadt quoted = "" 4101e72d8d2Sderaadt rest = fullname 4111e72d8d2Sderaadt for (;;) { 4121e72d8d2Sderaadt p = index(rest, "\\") 4131e72d8d2Sderaadt q = index(rest, "\"") 4141e72d8d2Sderaadt if (p) { 4151e72d8d2Sderaadt if (q && q<p) p = q 4161e72d8d2Sderaadt } else { 4171e72d8d2Sderaadt if (!q) break 4181e72d8d2Sderaadt p = q 4191e72d8d2Sderaadt } 4201e72d8d2Sderaadt quoted = quoted substr(rest, 1, p-1) "\\" substr(rest, p, 1) 4211e72d8d2Sderaadt rest = substr(rest, p+1) 4221e72d8d2Sderaadt } 4231e72d8d2Sderaadt 4241e72d8d2Sderaadt printf "fullname[\"%s\"] = \"%s%s\"\n", $1, quoted, rest 4251e72d8d2Sderaadt author[$1] = 0 4261e72d8d2Sderaadt } 4271e72d8d2Sderaadt } 4281e72d8d2Sderaadt ' 4291e72d8d2Sderaadt 4301e72d8d2Sderaadt initialize_fullname=` 431c71bc7e2Stholo { 432c71bc7e2Stholo (getent passwd $authors) || 43350bf276cStholo ( 43450bf276cStholo cat /etc/passwd 43550bf276cStholo for author in $authors 436c71bc7e2Stholo do NIS_PATH= nismatch $author passwd.org_dir 43750bf276cStholo done 43850bf276cStholo ypmatch $authors passwd 439c71bc7e2Stholo ) 440c71bc7e2Stholo } 2>/dev/null | 4411e72d8d2Sderaadt $AWK -F: "$awkscript" 4421e72d8d2Sderaadt `$initialize_fullname 4431e72d8d2Sderaadtesac 4441e72d8d2Sderaadt 4451e72d8d2Sderaadt 4461e72d8d2Sderaadt# Function to print a single log line. 4471e72d8d2Sderaadt# We don't use awk functions, to stay compatible with old awk versions. 448c71bc7e2Stholo# `Log' is the log message (with \n replaced by \001). 4491e72d8d2Sderaadt# `files' contains the affected files. 4501e72d8d2Sderaadtprintlogline='{ 4511e72d8d2Sderaadt 4521e72d8d2Sderaadt # Following the GNU coding standards, rewrite 4531e72d8d2Sderaadt # * file: (function): comment 4541e72d8d2Sderaadt # to 4551e72d8d2Sderaadt # * file (function): comment 4561e72d8d2Sderaadt if (Log ~ /^\([^)]*\): /) { 4571e72d8d2Sderaadt i = index(Log, ")") 4581e72d8d2Sderaadt files = files " " substr(Log, 1, i) 4591e72d8d2Sderaadt Log = substr(Log, i+3) 4601e72d8d2Sderaadt } 4611e72d8d2Sderaadt 4621e72d8d2Sderaadt # If "label: comment" is too long, break the line after the ":". 4631e72d8d2Sderaadt sep = " " 464c71bc7e2Stholo if ('"$length"' <= '"$indent"' + 1 + length(files) + index(Log, SOH)) sep = "\n" indent_string 4651e72d8d2Sderaadt 4661e72d8d2Sderaadt # Print the label. 4671e72d8d2Sderaadt printf "%s*%s:", indent_string, files 4681e72d8d2Sderaadt 469c71bc7e2Stholo # Print each line of the log, transliterating \001 to \n. 470c71bc7e2Stholo while ((i = index(Log, SOH)) != 0) { 4711e72d8d2Sderaadt logline = substr(Log, 1, i-1) 4721e72d8d2Sderaadt if (logline ~ /[^'"$tab"' ]/) { 4731e72d8d2Sderaadt printf "%s%s\n", sep, logline 4741e72d8d2Sderaadt } else { 4751e72d8d2Sderaadt print "" 4761e72d8d2Sderaadt } 4771e72d8d2Sderaadt sep = indent_string 4781e72d8d2Sderaadt Log = substr(Log, i+1) 4791e72d8d2Sderaadt } 4801e72d8d2Sderaadt}' 4811e72d8d2Sderaadt 482c71bc7e2Stholo# Pattern to match the `revision' line of rlog output. 483c71bc7e2Stholorlog_revision_pattern='^revision [0-9]+\.[0-9]+(\.[0-9]+\.[0-9]+)*(['"$tab"' ]+locked by: [^'"$tab"' $,.0-9:;@]*[^'"$tab"' $,:;@][^'"$tab"' $,.0-9:;@]*;)?['"$tab"' ]*$' 484c71bc7e2Stholo 4851e72d8d2Sderaadtcase $hostname in 4861e72d8d2Sderaadt'') 4871e72d8d2Sderaadt hostname=`( 4881e72d8d2Sderaadt hostname || uname -n || uuname -l || cat /etc/whoami 4891e72d8d2Sderaadt ) 2>/dev/null` || { 4901e72d8d2Sderaadt echo >&2 "$0: cannot deduce hostname" 4911e72d8d2Sderaadt exit 1 4921e72d8d2Sderaadt } 49350bf276cStholo 49450bf276cStholo case $hostname in 49550bf276cStholo *.*) ;; 49650bf276cStholo *) 49750bf276cStholo domainname=`(domainname) 2>/dev/null` && 49850bf276cStholo case $domainname in 49950bf276cStholo *.*) hostname=$hostname.$domainname 50050bf276cStholo esac 50150bf276cStholo esac 5021e72d8d2Sderaadtesac 5031e72d8d2Sderaadt 5041e72d8d2Sderaadt 5051e72d8d2Sderaadt# Process the rlog output, generating ChangeLog style entries. 5061e72d8d2Sderaadt 5071e72d8d2Sderaadt# First, reformat the rlog output so that each line contains one log entry. 508c71bc7e2Stholo# Transliterate \n to \001 so that multiline entries fit on a single line. 5091e72d8d2Sderaadt# Discard irrelevant rlog output. 5101e72d8d2Sderaadt$AWK <$rlogout ' 5111e72d8d2Sderaadt BEGIN { repository = "'"$repository"'" } 5121e72d8d2Sderaadt /^RCS file:/ { 5131e72d8d2Sderaadt if (repository != "") { 5141e72d8d2Sderaadt filename = $3 5151e72d8d2Sderaadt if (substr(filename, 1, length(repository) + 1) == repository "/") { 5161e72d8d2Sderaadt filename = substr(filename, length(repository) + 2) 5171e72d8d2Sderaadt } 5181e72d8d2Sderaadt if (filename ~ /,v$/) { 5191e72d8d2Sderaadt filename = substr(filename, 1, length(filename) - 2) 5201e72d8d2Sderaadt } 521c71bc7e2Stholo if (filename ~ /(^|\/)Attic\/[^\/]*$/) { 522c71bc7e2Stholo i = length(filename) 523c71bc7e2Stholo while (substr(filename, i, 1) != "/") i-- 524c71bc7e2Stholo filename = substr(filename, 1, i - 6) substr(filename, i + 1) 5251e72d8d2Sderaadt } 5261e72d8d2Sderaadt } 527c71bc7e2Stholo rev = "?" 528c71bc7e2Stholo } 5291e72d8d2Sderaadt /^Working file:/ { if (repository == "") filename = $3 } 530c71bc7e2Stholo /'"$rlog_revision_pattern"'/, /^(-----------*|===========*)$/ { 531c71bc7e2Stholo line = $0 532c71bc7e2Stholo if (line ~ /'"$rlog_revision_pattern"'/) { 533c71bc7e2Stholo rev = $2 534c71bc7e2Stholo next 535c71bc7e2Stholo } 536c71bc7e2Stholo if (line ~ /^date: [0-9][- +\/0-9:]*;/) { 5371e72d8d2Sderaadt date = $2 538c71bc7e2Stholo if (date ~ /\//) { 539c71bc7e2Stholo # This is a traditional RCS format date YYYY/MM/DD. 540c71bc7e2Stholo # Replace "/"s with "-"s to get ISO format. 5411e72d8d2Sderaadt newdate = "" 542c71bc7e2Stholo while ((i = index(date, "/")) != 0) { 543c71bc7e2Stholo newdate = newdate substr(date, 1, i-1) "-" 5441e72d8d2Sderaadt date = substr(date, i+1) 5451e72d8d2Sderaadt } 5461e72d8d2Sderaadt date = newdate date 5471e72d8d2Sderaadt } 548c71bc7e2Stholo time = substr($3, 1, length($3) - 1) 5491e72d8d2Sderaadt author = substr($5, 1, length($5)-1) 550c71bc7e2Stholo printf "%s %s %s %s %s %c", filename, rev, date, time, author, 1 551c71bc7e2Stholo rev = "?" 5521e72d8d2Sderaadt next 5531e72d8d2Sderaadt } 554c71bc7e2Stholo if (line ~ /^branches: /) { next } 555c71bc7e2Stholo if (line ~ /^(-----------*|===========*)$/) { print ""; next } 556c71bc7e2Stholo if (line == "Initial revision" || line ~ /^file .+ was initially added on branch .+\.$/) { 557c71bc7e2Stholo line = "New file." 558c71bc7e2Stholo } 559c71bc7e2Stholo printf "%s%c", line, 1 5601e72d8d2Sderaadt } 5611e72d8d2Sderaadt' | 5621e72d8d2Sderaadt 5631e72d8d2Sderaadt# Now each line is of the form 564c71bc7e2Stholo# FILENAME REVISION YYYY-MM-DD HH:MM:SS[+-TIMEZONE] AUTHOR \001LOG 565c71bc7e2Stholo# where \001 stands for a carriage return, 566c71bc7e2Stholo# and each line of the log is terminated by \001 instead of \n. 5671e72d8d2Sderaadt# Sort the log entries, first by date+time (in reverse order), 568c71bc7e2Stholo# then by author, then by log entry, and finally by file name and revision 569c71bc7e2Stholo# (just in case). 570c71bc7e2Stholosort +2 -4r +4 +0 | 5711e72d8d2Sderaadt 5721e72d8d2Sderaadt# Finally, reformat the sorted log entries. 5731e72d8d2Sderaadt$AWK ' 5741e72d8d2Sderaadt BEGIN { 575c71bc7e2Stholo logTZ = "'"$logTZ"'" 576c71bc7e2Stholo revision = "'"$revision"'" 577c71bc7e2Stholo 578c71bc7e2Stholo # Some awk variants do not understand "\001", so we have to 579c71bc7e2Stholo # put the char directly in the file. 580c71bc7e2Stholo SOH="" # <-- There is a single SOH (octal code 001) here. 5811e72d8d2Sderaadt 5821e72d8d2Sderaadt # Initialize the fullname and mailaddr associative arrays. 5831e72d8d2Sderaadt '"$initialize_fullname"' 5841e72d8d2Sderaadt '"$initialize_mailaddr"' 5851e72d8d2Sderaadt 5861e72d8d2Sderaadt # Initialize indent string. 5871e72d8d2Sderaadt indent_string = "" 5881e72d8d2Sderaadt i = '"$indent"' 5891e72d8d2Sderaadt if (0 < '"$tabwidth"') 5901e72d8d2Sderaadt for (; '"$tabwidth"' <= i; i -= '"$tabwidth"') 5911e72d8d2Sderaadt indent_string = indent_string "\t" 5921e72d8d2Sderaadt while (1 <= i--) 5931e72d8d2Sderaadt indent_string = indent_string " " 5941e72d8d2Sderaadt } 5951e72d8d2Sderaadt 5961e72d8d2Sderaadt { 597c71bc7e2Stholo newlog = substr($0, 1 + index($0, SOH)) 5981e72d8d2Sderaadt 5991e72d8d2Sderaadt # Ignore log entries prefixed by "#". 6001e72d8d2Sderaadt if (newlog ~ /^#/) { next } 6011e72d8d2Sderaadt 602c71bc7e2Stholo if (Log != newlog || date != $3 || author != $5) { 6031e72d8d2Sderaadt 6041e72d8d2Sderaadt # The previous log and this log differ. 6051e72d8d2Sderaadt 6061e72d8d2Sderaadt # Print the old log. 6071e72d8d2Sderaadt if (date != "") '"$printlogline"' 6081e72d8d2Sderaadt 6091e72d8d2Sderaadt # Logs that begin with "{clumpname} " should be grouped together, 6101e72d8d2Sderaadt # and the clumpname should be removed. 6111e72d8d2Sderaadt # Extract the new clumpname from the log header, 6121e72d8d2Sderaadt # and use it to decide whether to output a blank line. 6131e72d8d2Sderaadt newclumpname = "" 6141e72d8d2Sderaadt sep = "\n" 6151e72d8d2Sderaadt if (date == "") sep = "" 6161e72d8d2Sderaadt if (newlog ~ /^\{[^'"$tab"' }]*}['"$tab"' ]/) { 6171e72d8d2Sderaadt i = index(newlog, "}") 6181e72d8d2Sderaadt newclumpname = substr(newlog, 1, i) 6191e72d8d2Sderaadt while (substr(newlog, i+1) ~ /^['"$tab"' ]/) i++ 6201e72d8d2Sderaadt newlog = substr(newlog, i+1) 6211e72d8d2Sderaadt if (clumpname == newclumpname) sep = "" 6221e72d8d2Sderaadt } 6231e72d8d2Sderaadt printf sep 6241e72d8d2Sderaadt clumpname = newclumpname 6251e72d8d2Sderaadt 6261e72d8d2Sderaadt # Get ready for the next log. 6271e72d8d2Sderaadt Log = newlog 6281e72d8d2Sderaadt if (files != "") 6291e72d8d2Sderaadt for (i in filesknown) 6301e72d8d2Sderaadt filesknown[i] = 0 6311e72d8d2Sderaadt files = "" 6321e72d8d2Sderaadt } 633c71bc7e2Stholo if (date != $3 || author != $5) { 6341e72d8d2Sderaadt # The previous date+author and this date+author differ. 6351e72d8d2Sderaadt # Print the new one. 636c71bc7e2Stholo date = $3 637c71bc7e2Stholo time = $4 638c71bc7e2Stholo author = $5 6391e72d8d2Sderaadt 640c71bc7e2Stholo zone = "" 641c71bc7e2Stholo if (logTZ && ((i = index(time, "-")) || (i = index(time, "+")))) 642c71bc7e2Stholo zone = " " substr(time, i) 6431e72d8d2Sderaadt 644c71bc7e2Stholo # Print "date[ timezone] fullname <email address>". 6451e72d8d2Sderaadt # Get fullname and email address from associative arrays; 6461e72d8d2Sderaadt # default to author and author@hostname if not in arrays. 6471e72d8d2Sderaadt if (fullname[author]) 6481e72d8d2Sderaadt auth = fullname[author] 6491e72d8d2Sderaadt else 6501e72d8d2Sderaadt auth = author 651c71bc7e2Stholo printf "%s%s %s ", date, zone, auth 6521e72d8d2Sderaadt if (mailaddr[author]) 6531e72d8d2Sderaadt printf "<%s>\n\n", mailaddr[author] 6541e72d8d2Sderaadt else 6551e72d8d2Sderaadt printf "<%s@%s>\n\n", author, "'"$hostname"'" 6561e72d8d2Sderaadt } 6571e72d8d2Sderaadt if (! filesknown[$1]) { 6581e72d8d2Sderaadt filesknown[$1] = 1 6591e72d8d2Sderaadt if (files == "") files = " " $1 6601e72d8d2Sderaadt else files = files ", " $1 661c71bc7e2Stholo if (revision && $2 != "?") files = files " " $2 6621e72d8d2Sderaadt } 6631e72d8d2Sderaadt } 6641e72d8d2Sderaadt END { 6651e72d8d2Sderaadt # Print the last log. 6661e72d8d2Sderaadt if (date != "") { 6671e72d8d2Sderaadt '"$printlogline"' 6681e72d8d2Sderaadt printf "\n" 6691e72d8d2Sderaadt } 6701e72d8d2Sderaadt } 6711e72d8d2Sderaadt' && 6721e72d8d2Sderaadt 6731e72d8d2Sderaadt 6741e72d8d2Sderaadt# Exit successfully. 6751e72d8d2Sderaadt 6761e72d8d2Sderaadtexec rm -f $llogout $rlogout 677c71bc7e2Stholo 678c71bc7e2Stholo# Local Variables: 679c71bc7e2Stholo# tab-width:4 680c71bc7e2Stholo# End: 681