Bring cvs-1.12.9 into the CVS repository
[dragonfly.git] / contrib / cvs-1.12.9 / contrib / rcs2log.sh
1 #! /bin/sh
2
3 # RCS to ChangeLog generator
4
5 # Generate a change log prefix from RCS files (perhaps in the CVS repository)
6 # and the ChangeLog (if any).
7 # Output the new prefix to standard output.
8 # You can edit this prefix by hand, and then prepend it to ChangeLog.
9
10 # Ignore log entries that start with `#'.
11 # Clump together log entries that start with `{topic} ',
12 # where `topic' contains neither white space nor `}'.
13
14 Help='The default FILEs are the files registered under the working directory.
15 Options:
16
17   -c CHANGELOG  Output a change log prefix to CHANGELOG (default ChangeLog).
18   -h HOSTNAME  Use HOSTNAME in change log entries (default current host).
19   -i INDENT  Indent change log lines by INDENT spaces (default 8).
20   -l LENGTH  Try to limit log lines to LENGTH characters (default 79).
21   -L FILE  Use rlog-format FILE for source of logs.
22   -R  If no FILEs are given and RCS is used, recurse through working directory.
23   -r OPTION  Pass OPTION to subsidiary log command.
24   -t TABWIDTH  Tab stops are every TABWIDTH characters (default 8).
25   -u "LOGIN<tab>FULLNAME<tab>MAILADDR"  Assume LOGIN has FULLNAME and MAILADDR.
26   -v  Append RCS revision to file names in log lines.
27   --help  Output help.
28   --version  Output version number.
29
30 Report bugs to <bug-gnu-emacs@gnu.org>.'
31
32 Id='$Id: rcs2log,v 1.48 2001/09/05 23:07:46 eggert Exp $'
33
34 # Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2001, 2003
35 #  Free Software Foundation, Inc.
36
37 # This program is free software; you can redistribute it and/or modify
38 # it under the terms of the GNU General Public License as published by
39 # the Free Software Foundation; either version 2, or (at your option)
40 # any later version.
41 #
42 # This program is distributed in the hope that it will be useful,
43 # but WITHOUT ANY WARRANTY; without even the implied warranty of
44 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
45 # GNU General Public License for more details.
46 #
47 # You should have received a copy of the GNU General Public License
48 # along with this program; see the file COPYING.  If not, write to the
49 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
50 # Boston, MA 02111-1307, USA.
51
52 Copyright='Copyright 1992-2003 Free Software Foundation, Inc.
53 This program comes with NO WARRANTY, to the extent permitted by law.
54 You may redistribute copies of this program
55 under the terms of the GNU General Public License.
56 For more information about these matters, see the files named COPYING.
57 Author: Paul Eggert <eggert@twinsun.com>'
58
59 # functions
60 @MKTEMP_SH_FUNCTION@
61
62 # Use the traditional C locale.
63 LANG=C
64 LANGUAGE=C
65 LC_ALL=C
66 LC_COLLATE=C
67 LC_CTYPE=C
68 LC_MESSAGES=C
69 LC_NUMERIC=C
70 LC_TIME=C
71 export LANG LANGUAGE LC_ALL LC_COLLATE LC_CTYPE LC_MESSAGES LC_NUMERIC LC_TIME
72
73 # These variables each contain a single ASCII character.
74 # Unfortunately, there's no portable way of writing these characters
75 # in older Unix implementations, other than putting them directly into
76 # this text file.
77 SOH='\ 1' # SOH, octal code 001
78 tab='   '
79 nl='
80 '
81
82 # Parse options.
83
84 # defaults
85 : ${MKTEMP="@MKTEMP@"}
86 : ${AWK=awk}
87 : ${TMPDIR=/tmp}
88
89 changelog=ChangeLog # change log file name
90 datearg= # rlog date option
91 hostname= # name of local host (if empty, will deduce it later)
92 indent=8 # indent of log line
93 length=79 # suggested max width of log line
94 logins= # login names for people we know fullnames and mailaddrs of
95 loginFullnameMailaddrs= # login<tab>fullname<tab>mailaddr triplets
96 logTZ= # time zone for log dates (if empty, use local time)
97 recursive= # t if we want recursive rlog
98 revision= # t if we want revision numbers
99 rlog_options= # options to pass to rlog
100 rlogfile= # log file to read from
101 tabwidth=8 # width of horizontal tab
102
103 while :
104 do
105         case $1 in
106         -c)     changelog=${2?}; shift;;
107         -i)     indent=${2?}; shift;;
108         -h)     hostname=${2?}; shift;;
109         -l)     length=${2?}; shift;;
110         -L)     rlogfile=${2?}; shift;;
111         -[nu])  # -n is obsolescent; it is replaced by -u.
112                 case $1 in
113                 -n)     case ${2?}${3?}${4?} in
114                         *"$tab"* | *"$nl"*)
115                                 echo >&2 "$0: -n '$2' '$3' '$4': tabs, newlines not allowed"
116                                 exit 1;;
117                         esac
118                         login=$2
119                         lfm=$2$tab$3$tab$4
120                         shift; shift; shift;;
121                 -u)
122                         # If $2 is not tab-separated, use colon for separator.
123                         case ${2?} in
124                         *"$nl"*)
125                                 echo >&2 "$0: -u '$2': newlines not allowed"
126                                 exit 1;;
127                         *"$tab"*)
128                                 t=$tab;;
129                         *)
130                                 t=':';;
131                         esac
132                         case $2 in
133                         *"$t"*"$t"*"$t"*)
134                                 echo >&2 "$0: -u '$2': too many fields"
135                                 exit 1;;
136                         *"$t"*"$t"*)
137                                 uf="[^$t]*$t" # An unselected field, followed by a separator.
138                                 sf="\\([^$t]*\\)" # The selected field.
139                                 login=`expr "X$2" : "X$sf"`
140                                 lfm="$login$tab"`
141                                         expr "X$2" : "$uf$sf"
142                                   `"$tab"`
143                                         expr "X$2" : "$uf$uf$sf"
144                                 `;;
145                         *)
146                                 echo >&2 "$0: -u '$2': not enough fields"
147                                 exit 1;;
148                         esac
149                         shift;;
150                 esac
151                 case $logins in
152                 '') logins=$login;;
153                 ?*) logins=$logins$nl$login;;
154                 esac
155                 case $loginFullnameMailaddrs in
156                 '') loginFullnameMailaddrs=$lfm;;
157                 ?*) loginFullnameMailaddrs=$loginFullnameMailaddrs$nl$lfm;;
158                 esac;;
159         -r)
160                 case $rlog_options in
161                 '') rlog_options=${2?};;
162                 ?*) rlog_options=$rlog_options$nl${2?};;
163                 esac
164                 shift;;
165         -R)     recursive=t;;
166         -t)     tabwidth=${2?}; shift;;
167         -v)     revision=t;;
168         --version)
169                 set $Id
170                 rcs2logVersion=$3
171                 echo >&2 "rcs2log (GNU Emacs) $rcs2logVersion$nl$Copyright"
172                 exit 0;;
173         -*)     echo >&2 "Usage: $0 [OPTION]... [FILE ...]$nl$Help"
174                 case $1 in
175                 --help) exit 0;;
176                 *) exit 1;;
177                 esac;;
178         *)      break;;
179         esac
180         shift
181 done
182
183 month_data='
184         m[0]="Jan"; m[1]="Feb"; m[2]="Mar"
185         m[3]="Apr"; m[4]="May"; m[5]="Jun"
186         m[6]="Jul"; m[7]="Aug"; m[8]="Sep"
187         m[9]="Oct"; m[10]="Nov"; m[11]="Dec"
188 '
189
190 logdir=`$MKTEMP -d $TMPDIR/rcs2log.XXXXXX`
191 test -n "$logdir" || exit
192 llogout=$logdir/l
193 trap exit 1 2 13 15
194 trap "rm -fr $logdir 2>/dev/null" 0
195
196 # If no rlog-format log file is given, generate one into $rlogfile.
197 case $rlogfile in
198 '')
199         rlogfile=$logdir/r
200
201         # If no rlog options are given,
202         # log the revisions checked in since the first ChangeLog entry.
203         # Since ChangeLog is only by date, some of these revisions may be duplicates of
204         # what's already in ChangeLog; it's the user's responsibility to remove them.
205         case $rlog_options in
206         '')
207                 if test -s "$changelog"
208                 then
209                         e='
210                                 /^[0-9]+-[0-9][0-9]-[0-9][0-9]/{
211                                         # ISO 8601 date
212                                         print $1
213                                         exit
214                                 }
215                                 /^... ... [ 0-9][0-9] [ 0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9]+ /{
216                                         # old-fashioned date and time (Emacs 19.31 and earlier)
217                                         '"$month_data"'
218                                         year = $5
219                                         for (i=0; i<=11; i++) if (m[i] == $2) break
220                                         dd = $3
221                                         printf "%d-%02d-%02d\n", year, i+1, dd
222                                         exit
223                                 }
224                         '
225                         d=`$AWK "$e" <"$changelog"` || exit
226                         case $d in
227                         ?*) datearg="-d>$d";;
228                         esac
229                 fi;;
230         esac
231
232         # Use TZ specified by ChangeLog local variable, if any.
233         if test -s "$changelog"
234         then
235                 extractTZ='
236                         /^.*change-log-time-zone-rule['"$tab"' ]*:['"$tab"' ]*"\([^"]*\)".*/{
237                                 s//\1/; p; q
238                         }
239                         /^.*change-log-time-zone-rule['"$tab"' ]*:['"$tab"' ]*t.*/{
240                                 s//UTC0/; p; q
241                         }
242                 '
243                 logTZ=`tail "$changelog" | sed -n "$extractTZ"`
244                 case $logTZ in
245                 ?*) TZ=$logTZ; export TZ;;
246                 esac
247         fi
248
249         # If CVS is in use, examine its repository, not the normal RCS files.
250         if test ! -f CVS/Repository
251         then
252                 rlog=rlog
253                 repository=
254         else
255                 rlog='cvs -q log'
256                 repository=`sed 1q <CVS/Repository` || exit
257                 test ! -f CVS/Root || CVSROOT=`cat <CVS/Root` || exit
258                 case $CVSROOT in
259                 *:/*:/*)
260                         echo >&2 "$0: $CVSROOT: CVSROOT has multiple ':/'s"
261                         exit 1;;
262                 *:/*)
263                         # remote repository
264                         pository=`expr "X$repository" : '.*:\(/.*\)'`;;
265                 *)
266                         # local repository
267                         case $repository in
268                         /*) ;;
269                         *) repository=${CVSROOT?}/$repository;;
270                         esac
271                         if test ! -d "$repository"
272                         then
273                                 echo >&2 "$0: $repository: bad repository (see CVS/Repository)"
274                                 exit 1
275                         fi
276                         pository=$repository;;
277                 esac
278
279                 # Ensure that $pository ends in exactly one slash.
280                 while :
281                 do
282                         case $pository in
283                         *//) pository=`expr "X$pository" : 'X\(.*\)/'`;;
284                         */) break;;
285                         *) pository=$pository/; break;;
286                         esac
287                 done
288
289         fi
290
291         # Use $rlog's -zLT option, if $rlog supports it.
292         case `$rlog -zLT 2>&1` in
293         *' option'*) ;;
294         *)
295                 case $rlog_options in
296                 '') rlog_options=-zLT;;
297                 ?*) rlog_options=-zLT$nl$rlog_options;;
298                 esac;;
299         esac
300
301         # With no arguments, examine all files under the RCS directory.
302         case $# in
303         0)
304                 case $repository in
305                 '')
306                         oldIFS=$IFS
307                         IFS=$nl
308                         case $recursive in
309                         t)
310                                 RCSdirs=`find . -name RCS -type d -print`
311                                 filesFromRCSfiles='s|,v$||; s|/RCS/|/|; s|^\./||'
312                                 files=`
313                                         {
314                                                 case $RCSdirs in
315                                                 ?*) find $RCSdirs \
316                                                                 -type f \
317                                                                 ! -name '*_' \
318                                                                 ! -name ',*,' \
319                                                                 ! -name '.*_' \
320                                                                 ! -name .rcsfreeze.log \
321                                                                 ! -name .rcsfreeze.ver \
322                                                                 -print;;
323                                                 esac
324                                                 find . -name '*,v' -print
325                                         } |
326                                         sort -u |
327                                         sed "$filesFromRCSfiles"
328                                 `;;
329                         *)
330                                 files=
331                                 for file in RCS/.* RCS/* .*,v *,v
332                                 do
333                                         case $file in
334                                         RCS/. | RCS/.. | RCS/,*, | RCS/*_) continue;;
335                                         RCS/.rcsfreeze.log | RCS/.rcsfreeze.ver) continue;;
336                                         RCS/.\* | RCS/\* | .\*,v | \*,v) test -f "$file" || continue;;
337                                         RCS/*,v | RCS/.*,v) ;;
338                                         RCS/* | RCS/.*) test -f "$file" || continue;;
339                                         esac
340                                         case $files in
341                                         '') files=$file;;
342                                         ?*) files=$files$nl$file;;
343                                         esac
344                                 done
345                                 case $files in
346                                 '') exit 0;;
347                                 esac;;
348                         esac
349                         set x $files
350                         shift
351                         IFS=$oldIFS;;
352                 esac;;
353         esac
354
355         case $datearg in
356         ?*) $rlog $rlog_options "$datearg" ${1+"$@"} >$rlogfile;;
357         '') $rlog $rlog_options ${1+"$@"} >$rlogfile;;
358         esac || exit;;
359 esac
360
361
362 # Get the full name of each author the logs mention, and set initialize_fullname
363 # to awk code that initializes the `fullname' awk associative array.
364 # Warning: foreign authors (i.e. not known in the passwd file) are mishandled;
365 # you have to fix the resulting output by hand.
366
367 initialize_fullname=
368 initialize_mailaddr=
369
370 case $loginFullnameMailaddrs in
371 ?*)
372         case $loginFullnameMailaddrs in
373         *\"* | *\\*)
374                 sed 's/["\\]/\\&/g' >$llogout <<EOF || exit
375 $loginFullnameMailaddrs
376 EOF
377                 loginFullnameMailaddrs=`cat $llogout`;;
378         esac
379
380         oldIFS=$IFS
381         IFS=$nl
382         for loginFullnameMailaddr in $loginFullnameMailaddrs
383         do
384                 IFS=$tab
385                 set x $loginFullnameMailaddr
386                 login=$2
387                 fullname=$3
388                 mailaddr=$4
389                 initialize_fullname="$initialize_fullname
390                         fullname[\"$login\"] = \"$fullname\""
391                 initialize_mailaddr="$initialize_mailaddr
392                         mailaddr[\"$login\"] = \"$mailaddr\""
393         done
394         IFS=$oldIFS;;
395 esac
396
397 case $logins in
398 ?*)
399         sort -u -o $llogout <<EOF
400 $logins
401 EOF
402         ;;
403 '')
404         : ;;
405 esac >$llogout || exit
406
407 output_authors='/^date: / {
408         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 ~ /^[^;]*;$/) {
409                 print substr($5, 1, length($5)-1)
410         }
411 }'
412 authors=`
413         $AWK "$output_authors" <"$rlogfile" | sort -u | comm -23 - $llogout
414 `
415 case $authors in
416 ?*)
417         cat >$llogout <<EOF || exit
418 $authors
419 EOF
420         initialize_author_script='s/["\\]/\\&/g; s/.*/author[\"&\"] = 1/'
421         initialize_author=`sed -e "$initialize_author_script" <$llogout`
422         awkscript='
423                 BEGIN {
424                         alphabet = "abcdefghijklmnopqrstuvwxyz"
425                         ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
426                         '"$initialize_author"'
427                 }
428                 {
429                         if (author[$1]) {
430                                 fullname = $5
431                                 if (fullname ~ /[0-9]+-[^(]*\([0-9]+\)$/) {
432                                         # Remove the junk from fullnames like "0000-Admin(0000)".
433                                         fullname = substr(fullname, index(fullname, "-") + 1)
434                                         fullname = substr(fullname, 1, index(fullname, "(") - 1)
435                                 }
436                                 if (fullname ~ /,[^ ]/) {
437                                         # Some sites put comma-separated junk after the fullname.
438                                         # Remove it, but leave "Bill Gates, Jr" alone.
439                                         fullname = substr(fullname, 1, index(fullname, ",") - 1)
440                                 }
441                                 abbr = index(fullname, "&")
442                                 if (abbr) {
443                                         a = substr($1, 1, 1)
444                                         A = a
445                                         i = index(alphabet, a)
446                                         if (i) A = substr(ALPHABET, i, 1)
447                                         fullname = substr(fullname, 1, abbr-1) A substr($1, 2) substr(fullname, abbr+1)
448                                 }
449
450                                 # Quote quotes and backslashes properly in full names.
451                                 # Do not use gsub; traditional awk lacks it.
452                                 quoted = ""
453                                 rest = fullname
454                                 for (;;) {
455                                         p = index(rest, "\\")
456                                         q = index(rest, "\"")
457                                         if (p) {
458                                                 if (q && q<p) p = q
459                                         } else {
460                                                 if (!q) break
461                                                 p = q
462                                         }
463                                         quoted = quoted substr(rest, 1, p-1) "\\" substr(rest, p, 1)
464                                         rest = substr(rest, p+1)
465                                 }
466
467                                 printf "fullname[\"%s\"] = \"%s%s\"\n", $1, quoted, rest
468                                 author[$1] = 0
469                         }
470                 }
471         '
472
473         initialize_fullname=`
474                 {
475                         (getent passwd $authors) ||
476                         (
477                                 cat /etc/passwd
478                                 for author in $authors
479                                 do NIS_PATH= nismatch $author passwd.org_dir
480                                 done
481                                 ypmatch $authors passwd
482                         )
483                 } 2>/dev/null |
484                 $AWK -F: "$awkscript"
485         `$initialize_fullname;;
486 esac
487
488
489 # Function to print a single log line.
490 # We don't use awk functions, to stay compatible with old awk versions.
491 # `Log' is the log message.
492 # `files' contains the affected files.
493 printlogline='{
494
495         # Following the GNU coding standards, rewrite
496         #       * file: (function): comment
497         # to
498         #       * file (function): comment
499         if (Log ~ /^\([^)]*\): /) {
500                 i = index(Log, ")")
501                 filefunc = substr(Log, 1, i)
502                 while ((j = index(filefunc, "\n"))) {
503                         files = files " " substr(filefunc, 1, j-1)
504                         filefunc = substr(filefunc, j+1)
505                 }
506                 files = files " " filefunc
507                 Log = substr(Log, i+3)
508         }
509
510         # If "label: comment" is too long, break the line after the ":".
511         sep = " "
512         i = index(Log, "\n")
513         if ('"$length"' <= '"$indent"' + 1 + length(files) + i) sep = "\n" indent_string
514
515         # Print the label.
516         printf "%s*%s:", indent_string, files
517
518         # Print each line of the log.
519         while (i) {
520                 logline = substr(Log, 1, i-1)
521                 if (logline ~ /[^'"$tab"' ]/) {
522                         printf "%s%s\n", sep, logline
523                 } else {
524                         print ""
525                 }
526                 sep = indent_string
527                 Log = substr(Log, i+1)
528                 i = index(Log, "\n")
529         }
530 }'
531
532 # Pattern to match the `revision' line of rlog output.
533 rlog_revision_pattern='^revision [0-9]+\.[0-9]+(\.[0-9]+\.[0-9]+)*(['"$tab"' ]+locked by: [^'"$tab"' $,.0-9:;@]*[^'"$tab"' $,:;@][^'"$tab"' $,.0-9:;@]*;)?['"$tab"' ]*$'
534
535 case $hostname in
536 '')
537         hostname=`(
538                 hostname || uname -n || uuname -l || cat /etc/whoami
539         ) 2>/dev/null` || {
540                 echo >&2 "$0: cannot deduce hostname"
541                 exit 1
542         }
543
544         case $hostname in
545         *.*) ;;
546         *)
547                 domainname=`(domainname) 2>/dev/null` &&
548                 case $domainname in
549                 *.*) hostname=$hostname.$domainname;;
550                 esac;;
551         esac;;
552 esac
553
554
555 # Process the rlog output, generating ChangeLog style entries.
556
557 # First, reformat the rlog output so that each line contains one log entry.
558 # Transliterate \n to SOH so that multiline entries fit on a single line.
559 # Discard irrelevant rlog output.
560 $AWK '
561         BEGIN {
562                 pository = "'"$pository"'"
563                 SOH="'"$SOH"'"
564         }
565         /^RCS file: / {
566                 if (pository != "") {
567                         filename = substr($0, 11)
568                         if (substr(filename, 1, length(pository)) == pository) {
569                                 filename = substr(filename, length(pository) + 1)
570                         }
571                         if (filename ~ /,v$/) {
572                                 filename = substr(filename, 1, length(filename) - 2)
573                         }
574                         if (filename ~ /(^|\/)Attic\/[^\/]*$/) {
575                                 i = length(filename)
576                                 while (substr(filename, i, 1) != "/") i--
577                                 filename = substr(filename, 1, i - 6) substr(filename, i + 1)
578                         }
579                 }
580                 rev = "?"
581         }
582         /^Working file: / { if (repository == "") filename = substr($0, 15) }
583         /'"$rlog_revision_pattern"'/, /^(-----------*|===========*)$/ {
584                 line = $0
585                 if (line ~ /'"$rlog_revision_pattern"'/) {
586                         rev = $2
587                         next
588                 }
589                 if (line ~ /^date: [0-9][- +\/0-9:]*;/) {
590                         date = $2
591                         if (date ~ /\//) {
592                                 # This is a traditional RCS format date YYYY/MM/DD.
593                                 # Replace "/"s with "-"s to get ISO format.
594                                 newdate = ""
595                                 while ((i = index(date, "/")) != 0) {
596                                         newdate = newdate substr(date, 1, i-1) "-"
597                                         date = substr(date, i+1)
598                                 }
599                                 date = newdate date
600                         }
601                         time = substr($3, 1, length($3) - 1)
602                         author = substr($5, 1, length($5)-1)
603                         printf "%s%s%s%s%s%s%s%s%s%s", filename, SOH, rev, SOH, date, SOH, time, SOH, author, SOH
604                         rev = "?"
605                         next
606                 }
607                 if (line ~ /^branches: /) { next }
608                 if (line ~ /^(-----------*|===========*)$/) { print ""; next }
609                 if (line == "Initial revision" || line ~ /^file .+ was initially added on branch .+\.$/) {
610                         line = "New file."
611                 }
612                 printf "%s%s", line, SOH
613         }
614 ' <"$rlogfile" |
615
616 # Now each line is of the form
617 # FILENAME@REVISION@YYYY-MM-DD@HH:MM:SS[+-TIMEZONE]@AUTHOR@LOG
618 #       where @ stands for an SOH (octal code 001),
619 #       and each line of LOG is terminated by SOH instead of \n.
620 # Sort the log entries, first by date+time (in reverse order),
621 # then by author, then by log entry, and finally by file name and revision
622 # (just in case).
623 sort -t"$SOH" +2 -4r +4 +0 |
624
625 # Finally, reformat the sorted log entries.
626 $AWK -F"$SOH" '
627         BEGIN {
628                 logTZ = "'"$logTZ"'"
629                 revision = "'"$revision"'"
630
631                 # Initialize the fullname and mailaddr associative arrays.
632                 '"$initialize_fullname"'
633                 '"$initialize_mailaddr"'
634
635                 # Initialize indent string.
636                 indent_string = ""
637                 i = '"$indent"'
638                 if (0 < '"$tabwidth"')
639                         for (;  '"$tabwidth"' <= i;  i -= '"$tabwidth"')
640                                 indent_string = indent_string "\t"
641                 while (1 <= i--)
642                         indent_string = indent_string " "
643         }
644
645         {
646                 newlog = ""
647                 for (i = 6; i < NF; i++) newlog = newlog $i "\n"
648
649                 # Ignore log entries prefixed by "#".
650                 if (newlog ~ /^#/) { next }
651
652                 if (Log != newlog || date != $3 || author != $5) {
653
654                         # The previous log and this log differ.
655
656                         # Print the old log.
657                         if (date != "") '"$printlogline"'
658
659                         # Logs that begin with "{clumpname} " should be grouped together,
660                         # and the clumpname should be removed.
661                         # Extract the new clumpname from the log header,
662                         # and use it to decide whether to output a blank line.
663                         newclumpname = ""
664                         sep = "\n"
665                         if (date == "") sep = ""
666                         if (newlog ~ /^\{[^'"$tab"' }]*}['"$tab"' ]/) {
667                                 i = index(newlog, "}")
668                                 newclumpname = substr(newlog, 1, i)
669                                 while (substr(newlog, i+1) ~ /^['"$tab"' ]/) i++
670                                 newlog = substr(newlog, i+1)
671                                 if (clumpname == newclumpname) sep = ""
672                         }
673                         printf sep
674                         clumpname = newclumpname
675
676                         # Get ready for the next log.
677                         Log = newlog
678                         if (files != "")
679                                 for (i in filesknown)
680                                         filesknown[i] = 0
681                         files = ""
682                 }
683                 if (date != $3  ||  author != $5) {
684                         # The previous date+author and this date+author differ.
685                         # Print the new one.
686                         date = $3
687                         time = $4
688                         author = $5
689
690                         zone = ""
691                         if (logTZ && ((i = index(time, "-")) || (i = index(time, "+"))))
692                                 zone = " " substr(time, i)
693
694                         # Print "date[ timezone]  fullname  <email address>".
695                         # Get fullname and email address from associative arrays;
696                         # default to author and author@hostname if not in arrays.
697                         if (fullname[author])
698                                 auth = fullname[author]
699                         else
700                                 auth = author
701                         printf "%s%s  %s  ", date, zone, auth
702                         if (mailaddr[author])
703                                 printf "<%s>\n\n", mailaddr[author]
704                         else
705                                 printf "<%s@%s>\n\n", author, "'"$hostname"'"
706                 }
707                 if (! filesknown[$1]) {
708                         filesknown[$1] = 1
709                         if (files == "") files = " " $1
710                         else files = files ", " $1
711                         if (revision && $2 != "?") files = files " " $2
712                 }
713         }
714         END {
715                 # Print the last log.
716                 if (date != "") {
717                         '"$printlogline"'
718                         printf "\n"
719                 }
720         }
721 ' &&
722
723
724 # Exit successfully.
725
726 exec rm -fr $logdir
727
728 # Local Variables:
729 # tab-width:4
730 # End: