1 |
#!/usr/bin/perl -w |
2 |
|
3 |
# Usage: extcvscomm.pl <date> |
4 |
# where <date> specifies the beginning of the time inteval (end is present) |
5 |
# from which to cull logs. |
6 |
# |
7 |
# Ex: extcvscomm.pl 2007-12-7 |
8 |
|
9 |
# Not surprisingly, CVS botches the "cvs log -d" command. If you provide a date argument |
10 |
# and there is a change on that date, |
11 |
# it appears that CVS finds the last change on that date, and then prints the change right |
12 |
# before that. If there was no change on that date, then it prints the last change before |
13 |
# that date. |
14 |
# Man, does CVS stink! |
15 |
|
16 |
use Time::Local; # to compensate for CVS's stinkiness |
17 |
|
18 |
$NCOMM = 256; |
19 |
|
20 |
my($cmd); |
21 |
my($arg); |
22 |
my($line); |
23 |
my($nextfile) = 0; |
24 |
my($grabtext) = 0; |
25 |
my($dateArg); |
26 |
my($file); |
27 |
my($date); |
28 |
my($author); |
29 |
my($commprefix); |
30 |
my($key); |
31 |
my(@clarr); |
32 |
my(%clmap); |
33 |
my(%fmap); |
34 |
my(%tmap); |
35 |
my(@dateArr); |
36 |
my(@desiredDateArr); |
37 |
my(@desiredDateArrB); |
38 |
my(@desiredDateArrInt); |
39 |
my($secs); |
40 |
my($desiredSecs); |
41 |
my($desiredSecsB); |
42 |
my($dateArgB); |
43 |
my($range) = "na"; |
44 |
|
45 |
while ($arg = shift(@ARGV)) |
46 |
{ |
47 |
$dateArg = $arg; |
48 |
$aidx++; |
49 |
} |
50 |
|
51 |
if ($aidx > 1) |
52 |
{ |
53 |
PrintUsage(); |
54 |
exit(1); |
55 |
} |
56 |
|
57 |
if ($dateArg =~ /^\s*\<(.+)\s*$/) |
58 |
{ |
59 |
# Less than or equal to a date |
60 |
@desiredDateArr = GetTimeArgs($1); |
61 |
$desiredSecs = timelocal(@desiredDateArr); |
62 |
@desiredDateArrInt = AddDay(1, @desiredDateArr); |
63 |
$dateArg = GetDate(@desiredDateArrInt); |
64 |
$dateArg = "<$dateArg"; |
65 |
$range = "lt"; |
66 |
} |
67 |
elsif ($dateArg =~ /^\s*\>(.+)\s*$/) |
68 |
{ |
69 |
# Greater than or equal to a date |
70 |
@desiredDateArr = GetTimeArgs($1); |
71 |
$desiredSecs = timelocal(@desiredDateArr); |
72 |
@desiredDateArrInt = AddDay(-1, @desiredDateArr); |
73 |
$dateArg = GetDate(@desiredDateArrInt); |
74 |
$dateArg = ">$dateArg"; |
75 |
$range = "gt"; |
76 |
} |
77 |
elsif ($dateArg =~ /^\s*(.+)\s*:\s*(.+)\s*$/) |
78 |
{ |
79 |
# An interval of dates |
80 |
@desiredDateArrB = GetTimeArgs($1); |
81 |
@desiredDateArr = GetTimeArgs($2); |
82 |
$desiredSecsB = timelocal(@desiredDateArrB); |
83 |
$desiredSecs = timelocal(@desiredDateArr); |
84 |
@desiredDateArrInt = AddDay(-1, @desiredDateArrB); |
85 |
$dateArgB = GetDate(@desiredDateArrInt); |
86 |
@desiredDateArrInt = AddDay(1, @desiredDateArr); |
87 |
$dateArg = GetDate(@desiredDateArrInt); |
88 |
$dateArg = "$dateArgB<$dateArg"; |
89 |
$range = "int"; |
90 |
} |
91 |
elsif ($dateArg =~ /^\s*(.+)\s*$/) |
92 |
{ |
93 |
@desiredDateArr = GetTimeArgs($1); |
94 |
$desiredSecs = timelocal(@desiredDateArr); |
95 |
@desiredDateArrInt = AddDay(-1, @desiredDateArr); |
96 |
$dateArgB = GetDate(@desiredDateArrInt); |
97 |
@desiredDateArrInt = AddDay(1, @desiredDateArr); |
98 |
$dateArg = GetDate(@desiredDateArrInt); |
99 |
$dateArg = "$dateArgB<$dateArg"; |
100 |
$range = "na"; |
101 |
} |
102 |
|
103 |
$cmd = "((cvs log -Nd \"$dateArg\" \.) 2>&1) |"; |
104 |
|
105 |
# print "$cmd\n"; |
106 |
open(CMDRET, $cmd); |
107 |
|
108 |
while (defined($line = <CMDRET>)) |
109 |
{ |
110 |
chomp($line); |
111 |
|
112 |
if ($nextfile == 1) |
113 |
{ |
114 |
if ($line =~ /^description:.*/) |
115 |
{ |
116 |
$grabtext = 1; |
117 |
$nextfile = 0; |
118 |
} |
119 |
|
120 |
next; |
121 |
} |
122 |
elsif ($grabtext == 1) |
123 |
{ |
124 |
if ($line =~ /============.+/) |
125 |
{ |
126 |
$grabtext = 0; |
127 |
next; |
128 |
} |
129 |
else |
130 |
{ |
131 |
if ($line =~ /^date:\s*(.+?)\s*;\s+author:\s*(.+?);.*/) |
132 |
{ |
133 |
# Unfortunately, CVS! |
134 |
# To work around this deficiency, use the first 256 bytes of the |
135 |
# actual comment used during the commit. Some people won't provide |
136 |
# that - skip those lines. |
137 |
$date = $1; |
138 |
$author = $2; |
139 |
|
140 |
# skip things caused by CVS's stink |
141 |
@dateArr = GetTimeArgs($date); |
142 |
$secs = timelocal(@dateArr); |
143 |
# print "secs: $secs, desiredsecs: $desiredSecs\n"; |
144 |
|
145 |
if ($range eq "na") |
146 |
{ |
147 |
if ($secs != $desiredSecs) |
148 |
{ |
149 |
next; |
150 |
} |
151 |
} |
152 |
elsif ($range eq "lt") |
153 |
{ |
154 |
if ($secs > $desiredSecs) |
155 |
{ |
156 |
next; |
157 |
} |
158 |
} |
159 |
elsif ($range eq "gt") |
160 |
{ |
161 |
if ($secs < $desiredSecs) |
162 |
{ |
163 |
next; |
164 |
} |
165 |
} |
166 |
elsif ($range eq "int") |
167 |
{ |
168 |
if ($secs < $desiredSecsB || $secs > $desiredSecs) |
169 |
{ |
170 |
next; |
171 |
} |
172 |
} |
173 |
|
174 |
$commprefix = ""; |
175 |
|
176 |
# There could be a newline at the beginning of the comment. If so, skip that line and go onto the next |
177 |
while ($commprefix !~ /\s*\S/) |
178 |
{ |
179 |
$line = <CMDRET>; |
180 |
chomp($line); |
181 |
$commprefix = substr($line, 0, $NCOMM); |
182 |
} |
183 |
|
184 |
$key = "::A:${author}::C:$commprefix"; |
185 |
|
186 |
if (defined($clmap{$key})) |
187 |
{ |
188 |
$fmap{$key} = "$fmap{$key}||$file"; |
189 |
} |
190 |
else |
191 |
{ |
192 |
$clmap{$key} = $line; |
193 |
$fmap{$key} = $file; |
194 |
$tmap{$key} = "$date"; |
195 |
push(@clarr, $key); |
196 |
} |
197 |
|
198 |
} |
199 |
} |
200 |
} |
201 |
elsif ($line =~ /^RCS file:\s*(.+)/) |
202 |
{ |
203 |
$file = $1; |
204 |
$nextfile = 1; |
205 |
next; |
206 |
} |
207 |
} |
208 |
|
209 |
while (defined($key = shift(@clarr))) |
210 |
{ |
211 |
if ($key =~ /::A:(\S+)::C:.+/) |
212 |
{ |
213 |
print STDOUT "date: $tmap{$key} ($1)\nfiles: $fmap{$key}\ncomments: $clmap{$key}\n\n"; |
214 |
} |
215 |
} |
216 |
|
217 |
sub PrintUsage |
218 |
{ |
219 |
print "extcvscomm.pl DATE #Show all changes on DATE\n"; |
220 |
print "extcvscomm.pl <DATE #Show all changes on DATE or before\n"; |
221 |
print "extcvscomm.pl >DATE #Show all changes on DATE or after\n"; |
222 |
print "extcvscomm.pl DATE1:DATE2 #Show all changes on or after DATE1 and on or before DATE2\n"; |
223 |
return; |
224 |
} |
225 |
|
226 |
sub GetTimeArgs |
227 |
{ |
228 |
my($date) = @_; |
229 |
my(@args); |
230 |
my($dom); |
231 |
my($month); |
232 |
my($year); |
233 |
|
234 |
$date =~ s/\//-/g; |
235 |
|
236 |
if ($date =~ /([0-9][0-9][0-9][0-9])\-([0-9]+)\-([0-9]+)/) |
237 |
{ |
238 |
$year = $1; |
239 |
$month = $2; |
240 |
$dom = $3; |
241 |
} |
242 |
else |
243 |
{ |
244 |
print STDERR "Invalid date '$date'\n"; |
245 |
exit(1); |
246 |
} |
247 |
|
248 |
push(@args, 0); # sec |
249 |
push(@args, 0); # min |
250 |
push(@args, 0); # hr |
251 |
push(@args, $dom); # day of month |
252 |
push(@args, $month - 1); # month, 0-based |
253 |
push(@args, $year - 1900); # year is relative to 1900 |
254 |
|
255 |
return @args; |
256 |
} |
257 |
|
258 |
sub GetDate |
259 |
{ |
260 |
my($sec, $min, $hr, $dom, $month, $yr) = @_; |
261 |
|
262 |
$month++; #dates are 1-based, but args are 0-based |
263 |
$yr = $yr + 1900; |
264 |
$date = "$yr-$month-$dom"; |
265 |
return $date |
266 |
} |
267 |
|
268 |
sub AddDay |
269 |
{ |
270 |
my($ndays, $sec, $min, $hr, $dom, $month, $yr) = @_; |
271 |
my($resultSecs); |
272 |
my(@resultArr); |
273 |
|
274 |
$resultSecs = timelocal($sec, $min, $hr, $dom, $month, $yr) + $ndays * 86400; |
275 |
@resultArr = localtime($resultSecs); |
276 |
|
277 |
return @resultArr; |
278 |
} |