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