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