added cvs_history
[tridge/junkcode.git] / cvs_history / history.pl
1 #!/usr/bin/perl -w
2 # Copyright (C) Andrew Tridgell <tridge@samba.org>     2001
3 # script to show recent checkins in cvs
4
5 my $BASEDIR = "/home/tridge/history";
6 my $TIMEZONE = "PST";
7 my $TIMEOFFSET = 0;
8
9 use strict qw{vars};
10 use lib "/home/tridge/history";
11 use util;
12 use POSIX;
13 use Data::Dumper;
14 use CGI::Base;
15 use CGI::Form;
16 use File::stat;
17
18 my $req = new CGI::Form;
19
20 my $HEADCOLOR = "#a0a0e0";
21 my $CVSWEB_BASE = "/cgi-bin/cvsweb.cgi/nas/linux/trinity";
22
23 my (%trees) = ('trinity' => "");
24
25 my $unpacked_dir = "/home/tridge/clipper";
26
27 ###############################################
28 # work out a URL so I can refer to myself in links
29 my $myself = $req->self_url;
30 if ($myself =~ /(.*)[?].*/) {
31     $myself = $1;
32 }
33
34 ################################################
35 # start CGI headers
36 sub cgi_headers() {
37     print "Content-type: text/html\r\n";
38
39     util::cgi_gzip();
40
41     print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
42 <html>
43 <head><title>recent checkins</title></head>
44 <body bgcolor="white" text="#000000" link="#0000EE" vlink="#551A8B" alink="#FF0000">
45 ';
46
47 }
48
49 ################################################
50 # end CGI
51 sub cgi_footers() {
52     print "</body>";
53     print "</html>\n";
54 }
55
56 ################################################
57 # print an error on fatal errors
58 sub fatal($) {
59     my $msg=shift;
60     print "ERROR: $msg<br>\n";
61     cgi_footers();
62     exit(0);
63 }
64
65
66 ###############################################
67 # pretty up a cvs diff -u
68 sub cvs_pretty($)
69 {
70     my $diff = shift;
71     my $ret = "";
72     my @lines = split(/$/m, $diff);
73
74     my (%colors) = (
75                     '^diff.*' => 'red',
76                     '^=.*' => 'blue',
77                     '^Index:.*' => 'blue',
78                     '^\-.*' => '#a00000',
79                     '^\+.*' => '#00a000'
80                     );
81
82     for (my $i=0; $i <= $#lines; $i++) {
83         my $line = $lines[$i];
84
85         for my $r (keys %colors) {
86             if ($line =~ /$r/m) {
87                 $line = "<font color=\"$colors{$r}\">$line</font>";
88                 last;
89             }
90         }
91         $ret .= $line;
92     }
93     return $ret;
94 }
95
96 sub plural_write ($$)
97 {
98     my ($num,$text) = @_;
99     if ($num != 1) {
100         $text = $text . "s";
101     }
102     if ($num > 0) {
103         return $num . " " . $text;
104     }
105     else {
106         return "";
107     }
108 }
109
110 sub readableTime ($$)
111 {
112     my ($i, $break, $retval);
113     my ($secs,$long) = @_;
114
115     # this function works correct for time >= 2 seconds
116     if ($secs < 2) {
117         return "very little time";
118     }
119
120     my %desc = (1 , 'second',
121                    60, 'minute',
122                    3600, 'hour',
123                    86400, 'day',
124                    604800, 'week',
125                    2628000, 'month',
126                    31536000, 'year');
127     my @breaks = sort {$a <=> $b} keys %desc;
128     $i = 0;
129     while ($i <= $#breaks && $secs >= 2 * $breaks[$i]) { 
130         $i++;
131     }
132     $i--;
133     $break = $breaks[$i];
134     $retval = plural_write(int ($secs / $break), $desc{"$break"});
135
136     if ($long == 1 && $i > 0) {
137         my $rest = $secs % $break;
138         $i--;
139         $break = $breaks[$i];
140         my $resttime = plural_write(int ($rest / $break), 
141                                 $desc{"$break"});
142         if ($resttime) {
143             $retval = $retval . ", " . $resttime;
144         }
145     }
146
147     return $retval;
148 }
149
150 #End raided code
151
152 sub cvsweb_paths($)
153 {
154     my $paths = shift;
155     my $ret = "";
156     while ($paths =~ /\s*([^\s]+)(.*)/) {
157         $ret .= "<a href=\"$CVSWEB_BASE/$1\">$1</a> ";
158         $paths = $2;
159     }
160     
161     return $ret;
162 }
163
164 #############################################
165 # show one row of history table
166 sub history_row($$)
167 {
168     my $entry = shift;
169     my $tree = shift;
170     my $msg = util::cgi_escape($entry->{MESSAGE});
171     my $t = POSIX::asctime(POSIX::gmtime($entry->{DATE}));
172     my $readabletime = readableTime(time()-$entry->{DATE},1);
173     print "
174 <table border=3>
175 <tr><td>
176 <b>$t UTC</b><br>
177 $readabletime ago<br>
178 <b><a href=$myself?function=diff&tree=$tree&date=$entry->{DATE}&author=$entry->{AUTHOR}>show diffs</a></b><br>
179 </td>
180 <td>$msg</td>
181 </tr>
182 </table>
183 <table>
184 <tr><td><b>Author:</b></td><td>$entry->{AUTHOR}</td></tr>
185 <tr valign=top><td><b>Modified:</b></td><td>";
186 print cvsweb_paths($entry->{FILES});
187 print "</td></tr>
188 <tr valign=top><td><b>Added:</b></td><td>";
189 print cvsweb_paths($entry->{ADDED});
190 print "</td></tr>
191 <tr valign=top><td><b>Removed:</b></td><td>";
192 print cvsweb_paths($entry->{REMOVED});
193 print "</td></tr>
194 </table>
195 \n";
196 }
197
198
199 ###############################################
200 # show recent cvs entries
201 sub cvs_diff($$$)
202 {
203     my $author = shift;
204     my $date = shift;
205     my $tree = shift;
206     my $cmd;
207
208     util::InArray($tree, [keys %trees]) || fatal("unknown tree");
209
210     my $log = util::LoadStructure("history.$tree");
211
212     chdir("$unpacked_dir/$tree") || fatal("no tree $unpacked_dir/$tree available");
213
214     for (my $i=0; $i <= $#{$log}; $i++) {
215         my $entry = $log->[$i];
216         if ($author eq $entry->{AUTHOR} &&
217             $date == $entry->{DATE}) {
218             my $t1;
219             my $t2;
220
221             chomp($t1 = POSIX::ctime($date-60+($TIMEOFFSET*60*60)));
222             chomp($t2 = POSIX::ctime($date+60+($TIMEOFFSET*60*60)));
223
224             print "<h2>CVS Diff in $tree for $t1</h2>\n";
225
226             print "<table border=0><tr>\n";
227             history_row($entry, $tree);
228             print "</tr></TABLE>\n";
229
230             if (! ($entry->{TAG} eq "") && !$entry->{REVISIONS}) {
231                 print '
232 <br>
233 <b>sorry, cvs diff on branches not currently possible due to a limitation 
234 in cvs</b>
235 <br>';
236             }
237
238             $ENV{'CVS_PASSFILE'} = "$BASEDIR/.cvspass";
239
240             if ($entry->{REVISIONS}) {
241                     for my $f (keys %{$entry->{REVISIONS}}) {
242                             my $cmd = "cvs diff -u -r $entry->{REVISIONS}->{$f}->{REV1} -r $entry->{REVISIONS}->{$f}->{REV2} $f";
243                             print "<!-- $cmd --!>\n";
244                             my $diff = `$cmd 2> /dev/null`;
245
246                             $diff = util::cgi_escape($diff);
247                             $diff = cvs_pretty($diff);
248                             
249                             print "<pre>$diff</pre>\n";
250                     }
251             } else {
252                     my $cmd = "cvs diff -u -D \"$t1 $TIMEZONE\" -D \"$t2 $TIMEZONE\" $entry->{FILES}";
253                     print "<!-- $cmd --!>\n";
254                     my $diff = `$cmd 2> /dev/null`;
255
256                     $diff = util::cgi_escape($diff);
257                     $diff = cvs_pretty($diff);
258                     
259                     print "<pre>$diff</pre>\n";
260             }
261
262             return;
263         }
264     }    
265 }
266
267
268 ###############################################
269 # show recent cvs entries
270 sub cvs_history($)
271 {
272     my $tree = shift;
273     my (%authors) = ('ALL');
274     my $author;
275
276     util::InArray($tree, [keys %trees]) || fatal("unknown tree");
277
278     my $log = util::LoadStructure("history.$tree");
279
280     for (my $i=$#{$log}; $i >= 0; $i--) {
281         $authors{$log->[$i]->{AUTHOR}} = 1;
282     }
283
284     print $req->startform("GET", $myself);
285     print "Select Author: ";
286     print $req->popup_menu("author", [sort keys %authors]);
287     print $req->submit('function', 'Go');
288     print $req->endform();
289
290     print "<h2>Recent checkins for $tree</h2>\n";
291     
292     print "
293 ";
294
295     $author = $req->param("author");
296
297     for (my $i=$#{$log}; $i >= 0; $i--) {
298         my $entry = $log->[$i];
299         if (! $author ||
300             ($author eq "ALL") || 
301             ($author eq $entry->{AUTHOR})) {
302             history_row($entry, $tree);
303         }
304     }
305     print '
306 ';
307 }
308
309
310 ###############################################
311 # main program
312 cgi_headers();
313
314 chdir("$BASEDIR") || fatal("can't change to data directory");
315
316 if (defined $req->param("function")) {
317     my $fn_name = $req->param("function");
318     if ($fn_name eq "diff") {
319         cvs_diff($req->param('author'), $req->param('date'), $req->param('tree'));
320     } else {
321         cvs_history("trinity");
322     }
323 } else {
324     cvs_history("trinity");
325 }
326
327 cgi_footers();