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