comparison lib-src/=rcs2log @ 11073:554c86f77db0

Add -u "login<tab>fullname<tab>mailaddr" option, which replaces the (now obsolescent) -n login fullname mailaddr option. Don't omit path from repository root when logging CVS files. Add -R option for recursive rlog. (AWK): New environment variable (default `awk') for name of awk program. (output_authors, tab, loginFullnameMailaddrs, recursive): New variables. Quote authors and fullnames correctly.
author Paul Eggert <eggert@twinsun.com>
date Tue, 21 Mar 1995 05:37:42 +0000
parents 12b36ece5c25
children aee85fcd9d25
comparison
equal deleted inserted replaced
11072:c5fbb6f272f2 11073:554c86f77db0
10 # Clump together log entries that start with `{topic} ', 10 # Clump together log entries that start with `{topic} ',
11 # where `topic' contains neither white space nor `}'. 11 # where `topic' contains neither white space nor `}'.
12 12
13 # Author: Paul Eggert <eggert@twinsun.com> 13 # Author: Paul Eggert <eggert@twinsun.com>
14 14
15 # $Id: rcs2log,v 1.17 1994/08/09 20:43:48 rms Exp eggert $ 15 # $Id: rcs2log,v 1.19 1995/03/21 05:11:06 eggert Exp $
16 16
17 # Copyright 1992, 1993 Free Software Foundation, Inc. 17 # Copyright 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
18 18
19 # This program is free software; you can redistribute it and/or modify 19 # This program is free software; you can redistribute it and/or modify
20 # it under the terms of the GNU General Public License as published by 20 # it under the terms of the GNU General Public License as published by
21 # the Free Software Foundation; either version 2, or (at your option) 21 # the Free Software Foundation; either version 2, or (at your option)
22 # any later version. 22 # any later version.
28 # 28 #
29 # You should have received a copy of the GNU General Public License 29 # You should have received a copy of the GNU General Public License
30 # along with this program; see the file COPYING. If not, write to 30 # along with this program; see the file COPYING. If not, write to
31 # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 31 # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
32 32
33 tab=' '
33 nl=' 34 nl='
34 ' 35 '
35 36
36 # Parse options. 37 # Parse options.
37 38
38 # defaults 39 # defaults
40 : ${AWK=awk}
39 : ${TMPDIR=/tmp} 41 : ${TMPDIR=/tmp}
40 hostname= # name of local host (if empty, will deduce it later) 42 hostname= # name of local host (if empty, will deduce it later)
41 indent=8 # indent of log line 43 indent=8 # indent of log line
42 initialize_fullname= # awk assignments to set up fullname array
43 initialize_mailaddr= # awk assignments to set up mailaddr array
44 length=79 # suggested max width of log line 44 length=79 # suggested max width of log line
45 logins= # login names for people we know fullnames and mailaddresses of 45 logins= # login names for people we know fullnames and mailaddresses of
46 loginsout= # temporary file holding sorted logins 46 loginFullnameMailaddrs= # login<tab>fullname<tab>mailaddr triplets
47 recursive= # t if we want recursive rlog
47 rlog_options= # options to pass to rlog 48 rlog_options= # options to pass to rlog
48 tabwidth=8 # width of horizontal tab 49 tabwidth=8 # width of horizontal tab
49 50
50 while : 51 while :
51 do 52 do
52 case $1 in 53 case $1 in
53 -i) indent=${2?};; 54 -i) indent=${2?}; shift;;
54 -h) hostname=${2?};; 55 -h) hostname=${2?}; shift;;
55 -l) length=${2?};; 56 -l) length=${2?}; shift;;
56 -n) logins=$logins$nl${2?} 57 -[nu]) # -n is obsolescent; it is replaced by -u.
57 loginsout=$TMPDIR/rcs2log$$l 58 case $1 in
58 case $2${3?}${4?} in 59 -n) case ${2?}${3?}${4?} in
59 *\"* | *\\* | *"$nl"*) 60 *"$tab"* | *"$nl"*)
60 echo >&2 "$0: -n '$2' '$3' '$4': special characters not allowed" 61 echo >&2 "$0: -n '$2' '$3' '$4': tabs, newlines not allowed"
61 exit 1 62 exit 1
63 esac
64 loginFullnameMailaddrs=$loginFullnameMailaddrs$nl$2$tab$3$tab$4
65 shift; shift; shift;;
66 -u)
67 case ${2?} in
68 *"$nl"*)
69 echo >&2 "$0: -u '$2': newlines not allowed"
70 exit 1;;
71 *"$tab"*"$tab"*"$tab"*)
72 echo >&2 "$0: -u '$2': too many fields"
73 exit 1;;
74 *"$tab"*"$tab"*)
75 ;;
76 *)
77 echo >&2 "$0: -u '$2': not enough fields"
78 exit 1
79 esac
80 loginFullnameMailaddrs=$loginFullnameMailaddrs$nl$2
81 shift
62 esac 82 esac
63 initialize_fullname="$initialize_fullname 83 logins=$logins$nl$login
64 fullname[\"$2\"] = \"$3\"" 84 ;;
65 initialize_mailaddr="$initialize_mailaddr 85 -r) rlog_options=$rlog_options$nl${2?}; shift;;
66 mailaddr[\"$2\"] = \"$4\"" 86 -R) recursive=t;;
67 shift; shift;; 87 -t) tabwidth=${2?}; shift;;
68 -r) rlog_options=$rlog_options$nl${2?};;
69 -t) tabwidth=${2?};;
70 -*) echo >&2 "$0: usage: $0 [options] [file ...] 88 -*) echo >&2 "$0: usage: $0 [options] [file ...]
71 Options: 89 Options:
72 [-h hostname] [-i indent] [-l length] [-n login fullname mailaddr]... 90 [-h hostname] [-i indent] [-l length] [-R] [-r rlog_option]
73 [-r rlog_option]... [-t tabwidth]" 91 [-t tabwidth] [-u 'login<TAB>fullname<TAB>mailaddr']..."
74 exit 1;; 92 exit 1;;
75 *) break 93 *) break
76 esac 94 esac
77 shift; shift 95 shift
78 done 96 done
79 97
80 month_data=' 98 month_data='
81 m[0]="Jan"; m[1]="Feb"; m[2]="Mar" 99 m[0]="Jan"; m[1]="Feb"; m[2]="Mar"
82 m[3]="Apr"; m[4]="May"; m[5]="Jun" 100 m[3]="Apr"; m[4]="May"; m[5]="Jun"
131 } 149 }
132 printf "%d/%02d/%02d %02d:%02d:%02d\n", year, i+1, dd, hh, mm, ss 150 printf "%d/%02d/%02d %02d:%02d:%02d\n", year, i+1, dd, hh, mm, ss
133 exit 151 exit
134 } 152 }
135 ' 153 '
136 d=`awk "$e" <ChangeLog` || exit 154 d=`$AWK "$e" <ChangeLog` || exit
137 case $d in 155 case $d in
138 ?*) date=$d 156 ?*) date=$d
139 esac 157 esac
140 fi 158 fi
141 datearg="-d>$date" 159 datearg="-d>$date"
158 # With no arguments, examine all files under the RCS directory. 176 # With no arguments, examine all files under the RCS directory.
159 case $# in 177 case $# in
160 0) 178 0)
161 case $repository in 179 case $repository in
162 '') 180 '')
163 files=
164 for file in RCS/.* RCS/* .*,v *,v
165 do
166 case $file in
167 RCS/. | RCS/..) continue;;
168 RCS/.\* | RCS/\* | .\*,v | \*,v) test -f "$file" || continue
169 esac
170 files=$files$nl$file
171 done
172 case $files in
173 '') exit 0
174 esac
175 oldIFS=$IFS 181 oldIFS=$IFS
176 IFS=$nl 182 IFS=$nl
177 set $files 183 case $recursive in
184 t)
185 RCSdirs=`find . -name RCS -type d -print`
186 filesFromRCSfiles='s|,v$||; s|/RCS/|/|; s|^\./||'
187 files=`
188 {
189 case $RCSdirs in
190 ?*) find $RCSdirs -type f -print
191 esac
192 find . -name '*,v' -print
193 } |
194 sort -u |
195 sed "$filesFromRCSfiles"
196 `;;
197 *)
198 files=
199 for file in RCS/.* RCS/* .*,v *,v
200 do
201 case $file in
202 RCS/. | RCS/..) continue;;
203 RCS/.\* | RCS/\* | .\*,v | \*,v) test -f "$file" || continue
204 esac
205 files=$files$nl$file
206 done
207 case $files in
208 '') exit 0
209 esac
210 esac
211 set x $files
212 shift
178 IFS=$oldIFS 213 IFS=$oldIFS
179 esac 214 esac
180 esac 215 esac
181 216
217 llogout=$TMPDIR/rcs2log$$l
182 rlogout=$TMPDIR/rcs2log$$r 218 rlogout=$TMPDIR/rcs2log$$r
183 trap exit 1 2 13 15 219 trap exit 1 2 13 15
184 trap "rm -f $loginsout $rlogout; exit 1" 0 220 trap "rm -f $llogout $rlogout; exit 1" 0
185 221
186 $rlog "$datearg" $rlog_options ${1+"$@"} >$rlogout || exit 222 $rlog "$datearg" $rlog_options ${1+"$@"} >$rlogout || exit
187 223
188 224
189 # Get the full name of each author the logs mention, and set initialize_fullname 225 # Get the full name of each author the logs mention, and set initialize_fullname
190 # to awk code that initializes the `fullname' awk associative array. 226 # to awk code that initializes the `fullname' awk associative array.
191 # Warning: foreign authors (i.e. not known in the passwd file) are mishandled; 227 # Warning: foreign authors (i.e. not known in the passwd file) are mishandled;
192 # you have to fix the resulting output by hand. 228 # you have to fix the resulting output by hand.
193 229
194 case $loginsout in 230 initialize_fullname=
195 ?*) sort -u -o $loginsout <<EOF || exit 231 initialize_mailaddr=
232
233 case $loginFullnameMailaddrs in
234 ?*)
235 case $loginFullnameMailaddrs in
236 *\"* | *\\*)
237 sed 's/["\\]/\\&/g' >$llogout <<EOF || exit
238 $loginFullnameMailaddrs
239 EOF
240 loginFullnameMailaddrs=`cat $llogout`
241 esac
242
243 oldIFS=$IFS
244 IFS=$nl
245 for loginFullnameMailaddr in $loginFullnameMailaddrs
246 do
247 IFS=$tab
248 set x $loginFullnameMailaddr
249 login=$2
250 fullname=$3
251 mailaddr=$4
252 initialize_fullname="$initialize_fullname
253 fullname[\"$login\"] = \"$fullname\""
254 initialize_mailaddr="$initialize_mailaddr
255 mailaddr[\"$login\"] = \"$mailaddr\""
256 done
257 IFS=$oldIFS
258 esac
259
260 case $llogout in
261 ?*) sort -u -o $llogout <<EOF || exit
196 $logins 262 $logins
197 EOF 263 EOF
198 esac 264 esac
265 output_authors='/^date: / {
266 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 ~ /^[^;]*;$/) {
267 print substr($5, 1, length($5)-1)
268 }
269 }'
199 authors=` 270 authors=`
200 sed -n 's|^date: *[0-9]*[-/][0-9][0-9][-/][0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9][-+0-9]*; *author: *\([^; ]*\).*|\1|p' <$rlogout | 271 $AWK "$output_authors" <$rlogout |
201 case $loginsout in 272 case $llogout in
202 '') sort -u;; 273 '') sort -u;;
203 ?*) sort -u | comm -23 - $loginsout 274 ?*) sort -u | comm -23 - $llogout
204 esac 275 esac
205 ` 276 `
206 case $authors in 277 case $authors in
207 ?*) 278 ?*)
208 initialize_author= 279 cat >$llogout <<EOF || exit
209 for author in $authors 280 $authors
210 do 281 EOF
211 initialize_author="$initialize_author 282 initialize_author_script='s/["\\]/\\&/g; s/.*/author[\"&\"] = 1/'
212 author[\"$author\"] = 1 283 initialize_author=`sed -e "$initialize_author_script" <$llogout`
213 "
214 done
215 awkscript=' 284 awkscript='
216 BEGIN { 285 BEGIN {
217 alphabet = "abcdefghijklmnopqrstuvwxyz" 286 alphabet = "abcdefghijklmnopqrstuvwxyz"
218 ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 287 ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
219 '"$initialize_author"' 288 '"$initialize_author"'
237 A = a 306 A = a
238 i = index(alphabet, a) 307 i = index(alphabet, a)
239 if (i) A = substr(ALPHABET, i, 1) 308 if (i) A = substr(ALPHABET, i, 1)
240 fullname = substr(fullname, 1, abbr-1) A substr($1, 2) substr(fullname, abbr+1) 309 fullname = substr(fullname, 1, abbr-1) A substr($1, 2) substr(fullname, abbr+1)
241 } 310 }
242 printf "fullname[\"%s\"] = \"%s\"\n", $1, fullname 311
312 # Quote quotes and backslashes properly in full names.
313 # Do not use gsub; traditional awk lacks it.
314 quoted = ""
315 rest = fullname
316 for (;;) {
317 p = index(rest, "\\")
318 q = index(rest, "\"")
319 if (p) {
320 if (q && q<p) p = q
321 } else {
322 if (!q) break
323 p = q
324 }
325 quoted = quoted substr(rest, 1, p-1) "\\" substr(rest, p, 1)
326 rest = substr(rest, p+1)
327 }
328
329 printf "fullname[\"%s\"] = \"%s%s\"\n", $1, quoted, rest
243 author[$1] = 0 330 author[$1] = 0
244 } 331 }
245 } 332 }
246 ' 333 '
247 334
248 initialize_fullname=` 335 initialize_fullname=`
249 (cat /etc/passwd; ypmatch $authors passwd) 2>/dev/null | 336 (cat /etc/passwd; ypmatch $authors passwd) 2>/dev/null |
250 awk -F: "$awkscript" 337 $AWK -F: "$awkscript"
251 `$initialize_fullname 338 `$initialize_fullname
252 esac 339 esac
253 340
254 341
255 # Function to print a single log line. 342 # Function to print a single log line.
276 printf "%s*%s:", indent_string, files 363 printf "%s*%s:", indent_string, files
277 364
278 # Print each line of the log, transliterating \r to \n. 365 # Print each line of the log, transliterating \r to \n.
279 while ((i = index(Log, CR)) != 0) { 366 while ((i = index(Log, CR)) != 0) {
280 logline = substr(Log, 1, i-1) 367 logline = substr(Log, 1, i-1)
281 if (logline ~ /[^ ]/) { 368 if (logline ~ /[^'"$tab"' ]/) {
282 printf "%s%s\n", sep, logline 369 printf "%s%s\n", sep, logline
283 } else { 370 } else {
284 print "" 371 print ""
285 } 372 }
286 sep = indent_string 373 sep = indent_string
302 # Process the rlog output, generating ChangeLog style entries. 389 # Process the rlog output, generating ChangeLog style entries.
303 390
304 # First, reformat the rlog output so that each line contains one log entry. 391 # First, reformat the rlog output so that each line contains one log entry.
305 # Transliterate \n to \r so that multiline entries fit on a single line. 392 # Transliterate \n to \r so that multiline entries fit on a single line.
306 # Discard irrelevant rlog output. 393 # Discard irrelevant rlog output.
307 awk <$rlogout ' 394 $AWK <$rlogout '
308 /^Working file:/ { filename = $3 } 395 BEGIN { repository = "'"$repository"'" }
396 /^RCS file:/ {
397 if (repository != "") {
398 filename = $3
399 if (substr(filename, 1, length(repository) + 1) == repository "/") {
400 filename = substr(filename, length(repository) + 2)
401 }
402 if (filename ~ /,v$/) {
403 filename = substr(filename, 1, length(filename) - 2)
404 }
405 }
406 }
407 /^Working file:/ { if (repository == "") filename = $3 }
309 /^date: /, /^(-----------*|===========*)$/ { 408 /^date: /, /^(-----------*|===========*)$/ {
310 if ($0 ~ /^branches: /) { next } 409 if ($0 ~ /^branches: /) { next }
311 if ($0 ~ /^date: [0-9][- +\/0-9:]*;/) { 410 if ($0 ~ /^date: [0-9][- +\/0-9:]*;/) {
312 date = $2 411 date = $2
313 if (date ~ /-/) { 412 if (date ~ /-/) {
337 # Sort the log entries, first by date+time (in reverse order), 436 # Sort the log entries, first by date+time (in reverse order),
338 # then by author, then by log entry, and finally by file name (just in case). 437 # then by author, then by log entry, and finally by file name (just in case).
339 sort +1 -3r +3 +0 | 438 sort +1 -3r +3 +0 |
340 439
341 # Finally, reformat the sorted log entries. 440 # Finally, reformat the sorted log entries.
342 awk ' 441 $AWK '
343 BEGIN { 442 BEGIN {
344 # Some awks do not understand "\r" or "\013", so we have to 443 # Some awks do not understand "\r" or "\013", so we have to
345 # put a carriage return directly in the file. 444 # put a carriage return directly in the file.
346 CR=" 445 CR="
347 " # <-- There is a single CR between the " chars here. 446 " # <-- There is a single CR between the " chars here.
391 # Extract the new clumpname from the log header, 490 # Extract the new clumpname from the log header,
392 # and use it to decide whether to output a blank line. 491 # and use it to decide whether to output a blank line.
393 newclumpname = "" 492 newclumpname = ""
394 sep = "\n" 493 sep = "\n"
395 if (date == "") sep = "" 494 if (date == "") sep = ""
396 if (newlog ~ /^\{[^ }]*}[ ]/) { 495 if (newlog ~ /^\{[^'"$tab"' }]*}['"$tab"' ]/) {
397 i = index(newlog, "}") 496 i = index(newlog, "}")
398 newclumpname = substr(newlog, 1, i) 497 newclumpname = substr(newlog, 1, i)
399 while (substr(newlog, i+1) ~ /^[ ]/) i++ 498 while (substr(newlog, i+1) ~ /^['"$tab"' ]/) i++
400 newlog = substr(newlog, i+1) 499 newlog = substr(newlog, i+1)
401 if (clumpname == newclumpname) sep = "" 500 if (clumpname == newclumpname) sep = ""
402 } 501 }
403 printf sep 502 printf sep
404 clumpname = newclumpname 503 clumpname = newclumpname
458 ' && 557 ' &&
459 558
460 559
461 # Exit successfully. 560 # Exit successfully.
462 561
463 exec rm -f $loginsout $rlogout 562 exec rm -f $llogout $rlogout