1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
#! /usr/bin/perl -w
#
# $FreeBSD$
#
# This hack is to sanitise the results of what the user may have
# "done" while editing the commit log message.. :-) Peter Wemm.
#
# Note: this uses an enhancement to cvs's verifymsg functionality.
# Normally, the check is advisory only, the FreeBSD version reads
# back the file after the verifymsg file so that this script can
# make changes.
#
use strict;
############################################################
#
# Configurable options
#
############################################################
# These are the optional headers that can be filled at the end of
# each commit message. The associated value is a regular-expression
# that is used to type-check the entered value. If the match failed
# then the commit is rejected. (See rcstemplate).
my %HEADERS = (
"Reviewed by" => '.*',
"Submitted by" => '.*',
"Obtained from" => '.*',
"Approved by" => '.*',
"PR" => '.*',
"MFC after" => '\d+(?:\s+(?:days?|weeks?|months?))?'
);
#############################################################
#
# Main Body
#
############################################################
my $filename = shift;
die "Usage: logcheck filename\n" unless $filename;
# Read the log file in, stripping 'CVS:' lines and removing trailing
# white spaces.
open IN, "< $filename" or
die "logcheck: Cannot open for reading: $filename: $!\n";
my @log_in = map { s/^(.*?)\s*$/$1/; $1 } grep { !/^CVS:/ } <IN>;
close IN;
# Remove leading, trailing and duplicate blank lines.
my $i = 0;
while ($i < scalar(@log_in)) {
if ($log_in[$i] eq "" && $log_in[$i + 1] eq "") {
splice(@log_in, $i, 1);
next;
}
++$i;
}
shift @log_in unless $log_in[0];
# Filter out blank templated entries, and type check if necessary.
# (looking from the end of the commit message backwards)
my $j = scalar(@log_in) - 1;
my $error = 0;
while ($j >= 0 and my $header = $log_in[$j]) {
--$j;
if ($header =~ /^(.*?):\s*(.*)$/) {
my $header = $1;
my $value = $2;
my $pattern = $HEADERS{$header};
# Ignore unrecognised headers.
unless (defined($pattern)) {
### print "Warning: unknown template header: $header\n";
next;
}
# Filter out the template header if it's blank.
if ($value eq "") {
splice(@log_in, $j + 1, 1);
next;
}
# Type check the header
unless ($value =~ /^$pattern$/) {
print "Error: $header: should match /^$pattern\$/.\n";
++$error;
next;
}
} else {
### XXX
# We're here because we saw a line that didn't match
# a template header (no ':'). This could be a continuation
# line from the previous header, or the log message proper.
# We don't know, so run the risk of checking the last paragraph
# of the log message for headers.
next;
}
}
# Write the modified log file back out.
my $tmpfile = $filename . "tmp";
open OUT, "> $tmpfile" or
die "logcheck: Cannot open for writing: $tmpfile: $!\n";
print OUT map { "$_\n" } @log_in;
close OUT;
# Stop the commit if there was a problem with the template headers.
if ($error) {
print "There were $error errors in the template headers.\n";
print "Please fix the log message and commit again.\n";
print "A copy of your log message was saved in $tmpfile.\n";
exit 1;
}
# Nuke likely editor backups.
unlink "$filename.~";
unlink "$filename.bak";
# Overwrite the log message with our sanitised one. (See the comment
# block at the top of this script for an explaination of why.)
rename($tmpfile, $filename) or
die "logcheck: Could not rename $tmpfile to $filename: $!";
exit 0;
|