Annotation of CVSROOT/log_generate.pl, Revision 1.7
1.1 maekawa 1: #!/usr/bin/perl -w
2: #
1.7 ! maekawa 3: # $OpenXM: CVSROOT/log_generate.pl,v 1.6 2000/10/28 16:52:27 maekawa Exp $
1.1 maekawa 4: #
5: # Most parts obtained from FreeBSD.org and modified for OpenXM
6: #
7:
8: require 5.003; # might work with older perl5
9:
10: use Sys::Hostname; # get hostname() function
11:
12: ############################################################
13: #
14: # Configurable options
15: #
16: ############################################################
17: #
18: # Where do you want the RCS ID and delta info?
19: # 0 = none,
20: # 1 = in mail only,
21: # 2 = rcsids in both mail and logs.
22: #
23: $rcsidinfo = 2;
24:
25: # Debug level, 0 = off
26: $debug = 0;
27: ############################################################
28: #
29: # Constants
30: #
31: ############################################################
32: $STATE_NONE = 0;
33: $STATE_CHANGED = 1;
34: $STATE_ADDED = 2;
35: $STATE_REMOVED = 3;
36: $STATE_LOG = 4;
37:
38: $FILE_PREFIX = "#cvs.files";
39: $LAST_FILE = "/tmp/#cvs.files.lastdir";
40: $CHANGED_FILE = "/tmp/#cvs.files.changed";
41: $ADDED_FILE = "/tmp/#cvs.files.added";
42: $REMOVED_FILE = "/tmp/#cvs.files.removed";
43: $LOG_FILE = "/tmp/#cvs.files.log";
44: $SUMMARY_FILE = "/tmp/#cvs.files.summary";
45: $MAIL_FILE = "/tmp/#cvs.files.mail";
46: $SUBJ_FILE = "/tmp/#cvs.files.subj";
47:
1.7 ! maekawa 48: $CVSROOT = $ENV{'CVSROOT'} || "/home/cvsroot/openxm";
1.1 maekawa 49:
50: ############################################################
51: #
52: # Subroutines
53: #
54: ############################################################
55:
56: sub cleanup_tmpfiles {
57: local($wd, @files);
58:
59: $wd = `pwd`;
60: chdir("/tmp");
61: opendir(DIR, ".");
62: push(@files, grep(/^$FILE_PREFIX\..*$id$/, readdir(DIR)));
63: closedir(DIR);
64: foreach (@files) {
65: unlink $_;
66: }
67: chdir($wd);
68: }
69:
70: sub append_to_logfile {
71: local($filename, @files) = @_;
72:
73: open(FILE, ">>$filename") || die ("Cannot open for append file $filename.\n");
74: print(FILE join("\n", @lines), "\n");
75: close(FILE);
76: }
77:
78: sub append_line {
79: local($filename, $line) = @_;
80: open(FILE, ">>$filename") || die("Cannot open for append file $filename.\n");
81: print(FILE $line, "\n");
82: close(FILE);
83: }
84:
85: sub read_line {
86: local($line);
87: local($filename) = @_;
88: open(FILE, "<$filename") || die("Cannot open for read file $filename.\n");
89: $line = <FILE>;
90: close(FILE);
91: chop($line);
92: $line;
93: }
94:
95: sub read_logfile {
96: local(@text) = ();
97: local($filename, $leader) = @_;
98: open(FILE, "<$filename") and do {
99: while (<FILE>) {
100: chop;
101: push(@text, $leader.$_);
102: }
103: close(FILE);
104: };
105: @text;
106: }
107:
108: sub write_logfile {
109: local($filename, @lines) = @_;
110:
111: open(FILE, ">$filename") || die("Cannot open for write log file $filename.\n");
112: print FILE join("\n", @lines), "\n";
113: close(FILE);
114: }
115:
116: sub format_names {
117: local($dir, @files) = @_;
118: local(@lines, $indent);
119:
120: $indent = length($dir);
121: if ($indent < 20) {
122: $indent = 20;
123: }
124:
125: $format = " %-" . sprintf("%d", $indent) . "s ";
126:
127: $lines[0] = sprintf($format, $dir);
128:
129: if ($debug) {
130: print STDERR "format_names(): dir = ", $dir, "; tag = ", $tag, "; files = ", join(":", @files), ".\n";
131: }
132: foreach $file (@files) {
133: if (length($lines[$#lines]) + length($file) > 66) {
134: $lines[++$#lines] = sprintf($format, "", "");
135: }
136: $lines[$#lines] .= $file . " ";
137: }
138:
139: @lines;
140: }
141:
142: sub format_lists {
143: local($header, @lines) = @_;
144: local(@text, @files, $lastdir, $lastsep, $tag);
145:
146: if ($debug) {
147: print STDERR "format_lists(): ", join(":", @lines), "\n";
148: }
149: @text = ();
150: @files = ();
151:
152: $lastdir = '';
153: $lastsep = '';
154: foreach $line (@lines) {
155: if ($line =~ /.*\/$/) {
156: if ($lastdir ne '') {
157: push(@text, &format_names($lastdir, @files));
158: }
159: $lastdir = $line;
160: $lastdir =~ s,/$,,;
161: $tag = ""; # next thing is a tag
162: @files = ();
163: } elsif ($tag eq '') {
164: $tag = $line;
165: next if ($header . $tag eq $lastsep);
166: $lastsep = $header . $tag;
167: if ($tag eq 'HEAD') {
168: push(@text, " $header files:");
169: } else {
170: push(@text, sprintf(" %-22s (Branch: %s)", "$header files:",
171: $tag));
172: }
173: } else {
174: push(@files, $line);
175: }
176: }
177: push(@text, &format_names($lastdir, @files));
178:
179: @text;
180: }
181:
182: sub append_names_to_file {
183: local($filename, $dir, $tag, @files) = @_;
184:
185: if (@files) {
186: open(FILE, ">>$filename") || die("Cannot open for append file $filename.\n");
187: print FILE $dir, "\n";
188: print FILE $tag, "\n";
189: print FILE join("\n", @files), "\n";
190: close(FILE);
191: }
192: }
193:
194: #
195: # do an 'cvs -Qn status' on each file in the arguments, and extract info.
196: #
197:
198: sub change_summary_changed {
199: local($out, $tag, @filenames) = @_;
200: local(@revline);
201: local($file, $rev, $rcsfile, $line);
202:
203: while (@filenames) {
204: $file = shift @filenames;
205:
206: if ("$file" eq "") {
207: next;
208: }
209:
210: open(RCS, "-|") || exec 'cvs', '-Qn', 'status', $file;
211:
212: $rev = "";
213: $delta = "";
214: $rcsfile = "";
215:
216:
217: while (<RCS>) {
218: if (/^[ \t]*Repository revision/) {
219: chop;
220: @revline = split(' ', $_);
221: $rev = $revline[2];
222: $rcsfile = $revline[3];
223: $rcsfile =~ s,^$CVSROOT[/]+,,;
224: $rcsfile =~ s/,v$//;
225: }
226: }
227: close(RCS);
228:
229: if ($rev ne '' && $rcsfile ne '') {
230: open(RCS, "-|") || exec 'cvs', '-Qn', 'log', "-r$rev", $file;
231: while (<RCS>) {
232: if (/^date:/) {
233: chop;
234: $delta = $_;
235: $delta =~ s/^.*;//;
236: $delta =~ s/^[\s]+lines://;
237: }
238: }
239: close(RCS);
240: }
241:
242: &append_line($out, sprintf("%-9s%-12s%s", $rev, $delta, $rcsfile));
243: }
244: }
245:
246: # Write these one day.
247: sub change_summary_added {
248: }
249: sub change_summary_removed {
250: }
251:
252: sub build_header {
253: local($header, $datestr);
254: delete $ENV{'TZ'};
255:
256: $datestr = `/bin/date +"%Y/%m/%d %H:%M:%S %Z"`;
257: chop($datestr);
258: $header = sprintf("%-8s %s", $login, $datestr);
259: }
260:
261: # !!! Mailing-list and commitlog history file mappings here !!!
262: sub mlist_map {
263: local($dir) = @_; # perl warns about this....
264:
265: return 'cvs-admin' if($dir =~ /^CVSROOT\//);
266:
267: return 'cvs-commiters' if($dir =~ /^OpenXM\//);
268:
269: return 'cvs-admin';
270:
271: }
272:
273: sub do_changes_file {
274: local(@text) = @_;
275: local(%unique);
276:
277: %unique = ();
278: @mailaddrs = &read_logfile("$MAIL_FILE.$id", "");
279: }
280:
281: sub mail_notification {
282: local(@text) = @_;
283: local($line, $word, $subjlines, $subjwords, @mailaddrs);
284: # local(%unique);
285:
286: # %unique = ();
287:
288: print "Mailing the commit message...\n";
289:
290: @mailaddrs = &read_logfile("$MAIL_FILE.$id", "");
291:
292: if ($debug) {
293: open(MAIL, "| /usr/sbin/mailsend -H $owner$dom");
294: } else {
1.5 maekawa 295: open(MAIL, "| /usr/sbin/mailsend -H cvs-committers$dom");
1.1 maekawa 296: }
297:
298: # This is turned off since the To: lines go overboard.
299: # - but keep it for the time being in case we do something like cvs-stable
300: # print(MAIL 'To: cvs-committers' . $dom . ", cvs-all" . $dom);
301: # foreach $line (@mailaddrs) {
302: # next if ($unique{$line});
303: # $unique{$line} = 1;
304: # next if /^cvs-/;
305: # print(MAIL ", " . $line . $dom);
306: # }
307: # print(MAIL "\n");
308:
309: $subject = 'Subject: OpenXM cvs commit:';
310: @subj = &read_logfile("$SUBJ_FILE.$id", "");
311: $subjlines = 0;
312: $subjwords = 0; # minimum of two "words" per line
313: LINE: foreach $line (@subj) {
314: foreach $word (split(/ /, $line)) {
315: if ($subjwords > 2 && length($subject . " " . $word) > 75) {
316: if ($subjlines > 2) {
317: $subject .= " ...";
318: }
319: print(MAIL $subject, "\n");
320: if ($subjlines > 2) {
321: $subject = "";
322: last LINE;
323: }
324: $subject = " "; # rfc822 continuation line
325: $subjwords = 0;
326: $subjlines++;
327: }
328: $subject .= " " . $word;
329: $subjwords++;
330: }
331: }
332: if ($subject ne "") {
333: print(MAIL $subject, "\n");
334: }
335: print (MAIL "\n");
336:
337: print(MAIL join("\n", @text));
338: close(MAIL);
339: }
340:
341: #############################################################
342: #
343: # Main Body
344: #
345: ############################################################
346:
347: #
348: # Setup environment
349: #
350: umask (002);
351: $host = hostname();
1.3 maekawa 352: if ($host =~ /^kerberos\.math\.sci\.kobe-u\.ac\.jp$/i) {
353: $dom = '@kerberos.math.sci.kobe-u.ac.jp';
1.1 maekawa 354: $owner = 'maekawa';
355: }
356:
357: #
358: # Initialize basic variables
359: #
360: $id = getpgrp();
361: $state = $STATE_NONE;
362: $tag = '';
363: $login = $ENV{'USER'} || getlogin || (getpwuid($<))[0] || sprintf("uid#%d",$<);
364: @files = split(' ', $ARGV[0]);
365: @path = split('/', $files[0]);
366: if ($#path == 0) {
367: $dir = ".";
368: } else {
369: $dir = join('/', @path[1..$#path]);
370: }
371: $dir = $dir . "/";
372:
373: if ($debug) {
374: print("ARGV - ", join(":", @ARGV), "\n");
375: print("files - ", join(":", @files), "\n");
376: print("path - ", join(":", @path), "\n");
377: print("dir - ", $dir, "\n");
378: print("id - ", $id, "\n");
379: }
380:
381: # Was used for To: lines, still used for commitlogs naming.
382: &append_line("$MAIL_FILE.$id", &mlist_map($files[0] . "/"));
383: &append_line("$SUBJ_FILE.$id", $ARGV[0]);
384:
385: #
386: # Check for a new directory first. This will always appear as a
387: # single item in the argument list, and an empty log message.
388: #
389: if ($ARGV[0] =~ /New directory/) {
390: $header = &build_header();
391: @text = ();
392: push(@text, $header);
393: push(@text, "");
394: push(@text, " ".$ARGV[0]);
395: &do_changes_file(@text);
396: #&mail_notification(@text);
397: &cleanup_tmpfiles();
398: exit 0;
399: }
400:
401: #
402: # Check for an import command. This will always appear as a
403: # single item in the argument list, and a log message.
404: #
405: if ($ARGV[0] =~ /Imported sources/) {
406: $header = &build_header();
407:
408: @text = ();
409: push(@text, $header);
410: push(@text, "");
411:
412: push(@text, " ".$ARGV[0]);
413: &do_changes_file(@text);
414:
415: while (<STDIN>) {
416: chop; # Drop the newline
417: push(@text, " ".$_);
418: }
419:
420: &mail_notification(@text);
421: &cleanup_tmpfiles();
422: exit 0;
423: }
424:
425: #
426: # Iterate over the body of the message collecting information.
427: #
428: $tag = "HEAD";
429: while (<STDIN>) {
430: s/[ \t\n]+$//; # delete trailing space
431: if (/^Revision\/Branch:/) {
432: s,^Revision/Branch:,,;
433: $tag = $_;
434: next;
435: }
436: if (/^[ \t]+Tag:/) {
437: s,^[ \t]+Tag: ,,;
438: $tag = $_;
439: next;
440: }
441: if (/^[ \t]+No tag$/) {
442: $tag = "HEAD";
443: next;
444: }
445: if (/^Modified Files/) { $state = $STATE_CHANGED; next; }
446: if (/^Added Files/) { $state = $STATE_ADDED; next; }
447: if (/^Removed Files/) { $state = $STATE_REMOVED; next; }
448: if (/^Log Message/) { $state = $STATE_LOG; next; }
449:
450: push (@{ $changed_files{$tag} }, split) if ($state == $STATE_CHANGED);
451: push (@{ $added_files{$tag} }, split) if ($state == $STATE_ADDED);
452: push (@{ $removed_files{$tag} }, split) if ($state == $STATE_REMOVED);
453: if ($state == $STATE_LOG) {
454: if (/^PR:$/i ||
455: /^Reviewed by:$/i ||
456: /^Submitted by:$/i ||
457: /^Obtained from:$/i) {
458: next;
459: }
460: push (@log_lines, $_);
461: }
462: }
463:
464: #
465: # Strip leading and trailing blank lines from the log message. Also
466: # compress multiple blank lines in the body of the message down to a
467: # single blank line.
468: # (Note, this only does the mail and changes log, not the rcs log).
469: #
470: while ($#log_lines > -1) {
471: last if ($log_lines[0] ne "");
472: shift(@log_lines);
473: }
474: while ($#log_lines > -1) {
475: last if ($log_lines[$#log_lines] ne "");
476: pop(@log_lines);
477: }
478: for ($l = $#log_lines; $l > 0; $l--) {
479: if (($log_lines[$l - 1] eq "") && ($log_lines[$l] eq "")) {
480: splice(@log_lines, $l, 1);
481: }
482: }
483:
484: #
485: # Find the log file that matches this log message
486: #
487: for ($i = 0; ; $i++) {
488: last if (! -e "$LOG_FILE.$i.$id");
489: @text = &read_logfile("$LOG_FILE.$i.$id", "");
490: last if ($#text == -1);
491: last if (join(" ", @log_lines) eq join(" ", @text));
492: }
493:
494: #
495: # Spit out the information gathered in this pass.
496: #
497: foreach $tag ( keys %added_files ) {
498: &append_names_to_file("$ADDED_FILE.$i.$id", $dir, $tag,
499: @{ $added_files{$tag} });
500: }
501: foreach $tag ( keys %changed_files ) {
502: &append_names_to_file("$CHANGED_FILE.$i.$id", $dir, $tag,
503: @{ $changed_files{$tag} });
504: }
505: foreach $tag ( keys %removed_files ) {
506: &append_names_to_file("$REMOVED_FILE.$i.$id", $dir, $tag,
507: @{ $removed_files{$tag} });
508: }
509: &write_logfile("$LOG_FILE.$i.$id", @log_lines);
510:
511: if ($rcsidinfo) {
512: foreach $tag ( keys %added_files ) {
513: &change_summary_added("$SUMMARY_FILE.$i.$id", $tag,
514: @{ $added_files{$tag} });
515: }
516: foreach $tag ( keys %changed_files ) {
517: &change_summary_changed("$SUMMARY_FILE.$i.$id", $tag,
518: @{ $changed_files{$tag} });
519: }
520: foreach $tag ( keys %removed_files ) {
521: &change_summary_removed("$SUMMARY_FILE.$i.$id", $tag,
522: @{ $removed_files{$tag} });
523: }
524: }
525:
526: #
527: # Check whether this is the last directory. If not, quit.
528: #
529: if (-e "$LAST_FILE.$id") {
530: $_ = &read_line("$LAST_FILE.$id");
531: $tmpfiles=$files[0];
532: $tmpfiles =~ s,([^a-zA-Z0-9_/]),\\$1,g;
533: if (! grep(/$tmpfiles$/, $_)) {
534: print "More commits to come...\n";
535: exit 0
536: }
537: }
538:
539: #
540: # This is it. The commits are all finished. Lump everything together
541: # into a single message, fire a copy off to the mailing list, and drop
542: # it on the end of the Changes file.
543: #
544: $header = &build_header();
545:
546: #
547: # Produce the final compilation of the log messages
548: #
549: @text = ();
550: push(@text, $header);
551: push(@text, "");
552: for ($i = 0; ; $i++) {
553: last if (! -e "$LOG_FILE.$i.$id");
554: @lines = &read_logfile("$CHANGED_FILE.$i.$id", "");
555: if ($#lines >= 0) {
556: push(@text, &format_lists("Modified", @lines));
557: }
558: @lines = &read_logfile("$ADDED_FILE.$i.$id", "");
559: if ($#lines >= 0) {
560: push(@text, &format_lists("Added", @lines));
561: }
562: @lines = &read_logfile("$REMOVED_FILE.$i.$id", "");
563: if ($#lines >= 0) {
564: push(@text, &format_lists("Removed", @lines));
565: }
566:
567: @lines = &read_logfile("$LOG_FILE.$i.$id", " ");
568: if ($#lines >= 0) {
569: push(@text, " Log:");
570: push(@text, @lines);
571: }
572: if ($rcsidinfo == 2) {
573: if (-e "$SUMMARY_FILE.$i.$id") {
574: push(@text, " ");
575: push(@text, " Revision Changes Path");
576: push(@text, &read_logfile("$SUMMARY_FILE.$i.$id", " "));
577: }
578: }
579: push(@text, "", "");
580: }
581: #
582: # Put the log message at the beginning of the Changes file
583: #
584: &do_changes_file(@text);
585:
586: #
587: # Now generate the extra info for the mail message..
588: #
589: if ($rcsidinfo == 1) {
590: $revhdr = 0;
591: for ($i = 0; ; $i++) {
592: last if (! -e "$LOG_FILE.$i.$id");
593: if (-e "$SUMMARY_FILE.$i.$id") {
594: if (!$revhdr++) {
595: push(@text, "Revision Changes Path");
596: }
597: push(@text, &read_logfile("$SUMMARY_FILE.$i.$id", ""));
598: }
599: }
600: if ($revhdr) {
601: push(@text, ""); # consistancy...
602: }
603: }
604:
605: #
606: # Mail out the notification.
607: #
608: &mail_notification(@text);
609: &cleanup_tmpfiles();
610: exit 0;
611: # EOF
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>