Mercurial > emacs
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 |