2 # $Id: db2html.in,v 1.22 2004/04/19 10:03:53 cjwatson Exp $
3 # usage: db2html [-diff] [-stampfile=<stampfile>] [-lastrun=<days>] <wwwbase>
5 #load the necessary libraries/configuration
6 $config_path = '/etc/debbugs';
7 $lib_path = '/usr/lib/debbugs';
9 require("$config_path/config");
10 require("$config_path/text");
11 require("$lib_path/errorlib");
12 $ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
14 use POSIX qw(strftime tzset);
18 #set current working directory
19 chdir("$gSpoolDir") || die "chdir spool: $!\n";
23 $stampfile = 'stamp.html';
24 $tail_html = $gHTMLTail;
25 $expirynote_html = '';
26 $expirynote_html = $gHTMLExpireNote if $gRemoveAge;
27 $shorthead = ' Ref * Package Keywords/Subject Submitter';
31 %displayshowpendings = ('pending','outstanding',
33 'forwarded','forwarded to upstream software authors');
35 #set timestamp for html files
36 $dtime = strftime "%a, %e %b %Y %T UTC", localtime;
37 $tail_html =~ s/SUBSTITUTE_DTIME/$dtime/;
39 #check for commandline switches
40 while (@ARGV && $ARGV[0] =~ m/^-/)
41 { if ($ARGV[0] eq '-diff') { $diff=1; }
42 elsif ($ARGV[0] =~ m/^-lastrun\=([0-9.]+)$/) { $lastrun= $1; undef $stampfile; }
43 elsif ($ARGV[0] =~ m/^-full$/) { undef $lastrun; undef $stampfile; }
44 elsif ($ARGV[0] =~ m/^-stampfile\=(\S+)$/) { $stampfile= $1; }
45 else { die "bad usage"; }
49 #check for remaing argument, only one...
51 $wwwbase= shift(@ARGV);
54 defined($startdate= time) || die "failed to get time: $!";
58 #if stamp file was given,
59 if (defined($stampfile))
60 { if (open(X,"< $stampfile"))
63 printf "progress last run %.7f days\n",$lastrun;
64 } else { print "progress stamp file $stampfile: $! - full\n"; }
67 #only process file if greater than last run...
68 if (defined($lastrun) && -M "db-h" > $lastrun)
70 s/SUBSTITUTE_DTIME/$dtime/o;
71 s/\<\!\-\-updateupdate\-\-\>.*\<\!\-\-\/updateupdate\-\-\>/check/;
72 &file('ix/zstamp.html','non',$_."</body></html>\n");
74 # print "db2html: no changes since last run\n";
78 #parse maintainer file
79 open(MM,"$gMaintainerFile") || die "open $gMaintainerFile: $!";
81 { m/^(\S+)\s+(\S.*\S)\s*$/ || die "$gMaintainerFile: \`$_'";
88 #load all database files
89 opendir(D,'db-h') || die "opendir db-h: $!";
90 @dirs = grep(s#^#db-h/#,grep(/^\d+$/,readdir(D)));
92 foreach my $dir (@dirs) {
94 push @files, grep(/^-?\d+\.log$/,readdir(D));
97 @files = sort { $a <=> $b } @files;
99 for $pending (qw(pending done forwarded))
100 { for $severity (@showseverities)
101 { eval "\$index${pending}${severity}= \$iiindex${pending}${severity}= ''; 1;"
102 or die "reset \$index${pending}${severity}: $@";
107 { next unless $f =~ m/^(-?\d+)\.log$/;
109 #((print STDERR "$ref\n"),
112 # unless $ref =~ m/^-/ || $ref =~ m/^124/;
113 &filelock("lock/$ref");
114 $preserveonly= defined($lastrun) && -M "db-h/".get_hashname($ref)."/$ref.log" > $lastrun;
115 if ($ref =~ m/^-\d$/)
116 { $week= $ref eq '-1' ? 'this week' :
117 $ref eq '-2' ? 'last week' :
118 $ref eq '-3' ? 'two weeks ago' :
119 ($ref-1)." weeks ago";
120 $linkto= "ju/unmatched$ref";
121 $short= "junk, $week";
123 "This includes messages sent to <code>done\@$gEmailDomain</code>\n".
124 "which did not have a $gBug reference number in the Subject line\n".
125 "or which contained an\n".
126 "unknown or out of date $gBug report number (these cause a warning\n".
127 "to be sent to the sender) and details about the messages\n".
128 "sent to <code>request@$gEmailDomain</code> (all of which".
129 "produce replies).\n";
130 $indexlink= "Messages not matched to a specific $gBug report - $week";
131 $data->{subject}= '';
136 $tpackfile= "pnone.html";
137 $indexpart= 'unmatched';
141 $_= $data->{package}; y/A-Z/a-z/; $_= $` if m/[^-+._a-z0-9()]/;
143 if ($data->{severity} eq '' || $data->{severity} eq 'normal')
145 $addseverity= $gDefaultSeverity;
146 } elsif (isstrongseverity($data->{severity}))
147 { $showseverity= "<strong>Severity: $data->{severity}</strong>;\n";
148 $addseverity= $data->{severity};
150 { $showseverity= "Severity: <em>$data->{severity}</em>;\n";
151 $addseverity= $data->{severity};
153 $days= int(($startdate - $data->{date})/86400); close(S);
154 $indexlink= "#$ref: ".&sani($data->{subject});
156 $packfile= length($tpack) ? "pa/l$tpack.html" : "pa/none.html";
157 $indexentry .= "Package: <A href=\"../$packfile\"><strong>".
158 &sani($data->{package})."</strong></A>;\n"
159 if length($data->{package});
160 $indexentry .= $showseverity;
161 $indexentry .= "Reported by: ".&sani($data->{originator});
162 $indexentry .= ";\nOwned by: ".&sani($data->{owner})
163 if length($data->{owner});
164 $indexentry .= ";\nKeywords: ".&sani($data->{keywords})
165 if length($data->{keywords});
166 $linkto= $ref; $linkto =~ s,^..,$&/$&,;
167 @merged= split(/ /,$data->{mergedwith});
169 { $mseparator= ";\nmerged with ";
171 { $mfile= $m; $mfile =~ s,^..,$&/$&,;
172 $indexentry .= $mseparator."<A href=\"../$mfile.html\">#$m</A>";
176 $daysold=$submitted='';
177 if (length($data->{done}))
178 { $indexentry .= ";\n<strong>Done:</strong> ".&sani($data->{done});
179 $indexpart= "done$addseverity";
180 } elsif (length($data->{forwarded}))
181 { $indexentry .= ";\n<strong>Forwarded</strong> to ".&sani($data->{forwarded});
182 $indexpart= "forwarded$addseverity";
184 { $cmonths= int($days/30);
185 if ($cmonths != $amonths)
186 { $msg= $cmonths == 0 ? "Submitted in the last month" :
187 $cmonths == 1 ? "Over one month old" :
188 $cmonths == 2 ? "Over two months old - attention is required" :
189 "OVER $cmonths MONTHS OLD - ATTENTION IS REQUIRED";
190 $shortindex .= "</pre><h2>$msg:</h2><pre>\n$shorthead\n";
193 $pad= 6-length(sprintf("%d",$f));
195 ($pad>0 ? ' 'x$pad : '').
196 sprintf("<A href=\"../%s.html\">%d</A>",$linkto,$ref).
197 &sani(sprintf(" %-1.1s %-10.10s %-35.35s %-.25s\n",
200 (length($data->{keywords}) ? $data->{keywords}.'/' : '').
201 $data->{subject}, $data->{originator}));
202 $shortindex.= $thissient;
203 $sient{"$ref $data->{package}"}= $thissient;
205 { $font= $days <= 30 ? '' :
208 $efont= length($font) ? "</$font>" : '';
209 $font= length($font) ? "<$font>" : '';
210 $daysold= "; $font$days days old$efont";
213 $submitted = 'THIS IS A BUG IN THE BUG PROCESSOR';
215 $submitted = strftime "%a, %e %b %Y %T %Z", localtime($data->{date});
217 $submitted= "; dated $submitted";
218 $indexpart= "pending$addseverity";
221 $short= $ref; $short =~ s/^\d+/#$&/;
222 $tmaint= defined($maintainer{$tpack}) ? $maintainer{$tpack} : '(unknown)';
223 $qpackage= &sani($_);
224 $descriptivehead= $indexentry.$submitted.";\nMaintainer for $qpackage is\n".
225 '<A href="../ma/l'.&maintencoded($tmaint).'.html">'.&sani($tmaint).'</A>.';
226 $indexentry .= $daysold;
230 $indexadd .= "<!--iid $iiref-->" if defined($iiref);
231 $indexadd .= "<li><A href=\"../$linkto.html\">".$indexlink."</A>";
232 $indexadd .= "<br>\n".$indexentry if length($indexentry);
233 $indexadd .= "<!--/iid-->" if defined($iiref);
235 $estr= "\$index$indexpart = \$indexadd.\$index$indexpart; 1;";
236 eval($estr) || die "eval add to \$index$indexpart ($estr) failed: $@";
237 #print STDERR ">$estr|$indexadd<\n";
238 $indexadd= "<!--ii $iiref-->\n" if defined($iiref);
239 eval("\$iiindex$indexpart = \$indexadd.\$iiindex$indexpart; 1;") ||
240 die "eval add to \$iiindex$indexpart failed: $@";
241 if (defined($tmaint))
242 { $countpermaint{$tmaint} += length($data->{done}) ? 0 : length($data->{forwarded}) ? 0 : 1;
243 eval("\$permaint${indexpart}{\$tmaint} .= \$indexadd; 1;") ||
244 die "eval add to \$permaint${indexpart}{\$tmaint} failed: $@";
247 { $countperpack{$tpack} += length($data->{done}) ? 0 : length($data->{forwarded}) ? 0 : 1;
248 eval("\$perpack${indexpart}{\$tpack} .= \$indexadd; 1;") ||
249 die "eval add to \$perpack${indexpart}{\$tpack} failed: $@";
251 if ($preserveonly) { &preserve("$linkto.html"); &preserve("$linkto-b.html"); &unfilelock; next; }
252 my $hash = get_hashname($ref);
253 open(L,"db-h/$hash/$ref.log") || die "open db-h/$hash/$ref.log: $!";
255 $boring=''; $xmessage= 0;
256 $normstate= 'kill-init';
260 $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
261 die "$ref ^G in state $normstate";
262 $normstate= 'incoming-recv';
264 $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
265 die "$ref ^A in state $normstate";
266 $normstate= 'autocheck';
268 $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
269 die "$ref ^B in state $normstate";
270 $normstate= 'recips';
272 $normstate eq 'go' || $normstate eq 'go-nox' || $normstate eq 'html' ||
273 die "$ref ^C in state $normstate";
274 $this .= "</pre>\n" if $normstate eq 'go' || $normstate eq 'go-nox';
275 if ($normstate eq 'html') {
277 $this .= " <em><A href=\"../$linkto-b.html#m$xmessage\">Full text</A>".
280 if ($suppressnext && $normstate ne 'html') {
281 $ntis= $this; $ntis =~ s:\<pre\>:</A><pre>:i;
282 $boring .= "<hr><A name=\"m$xmessage\">\n$ntis\n";
284 $log = $this. "<hr>\n". $log;
286 $suppressnext= $normstate eq 'html';
287 $normstate= 'kill-end';
289 $normstate eq 'kill-body' || die "^E in state $normstate";
293 $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
294 die "$ref ^F in state $normstate";
295 $normstate= 'html'; $this= '';
296 } elsif ($normstate eq 'incoming-recv') {
297 $pl= $_; $pl =~ s/\n+$//;
298 m/^Received: \(at (\S+)\) by (\S+)\;/ ||
299 die "bad line \`$pl' in state incoming-recv";
300 $this = "<h2>Message received at ".&sani("$1\@$2").":</h2><br>\n".
304 } elsif ($normstate eq 'html') {
306 } elsif ($normstate eq 'go') {
309 } elsif ($normstate eq 'go-nox') {
312 } elsif ($normstate eq 'recips') {
314 $this = "<h2>Message sent:</h2><br>\n";
317 $this = "<h2>Message sent to ".&sani($_).":</h2><br>\n";
319 $normstate= 'kill-body';
320 } elsif ($normstate eq 'autocheck') {
321 next if !m/^X-Debian-Bugs(-\w+)?: This is an autoforward from (\S+)/;
322 $normstate= 'autowait';
323 $this = "<h2>Message received at $2:</h2><br>\n";
324 } elsif ($normstate eq 'autowait') {
326 $normstate= 'go-nox';
329 die "$ref state $normstate line \`$_'";
332 die "$ref state $normstate at end" unless $normstate eq 'kill-end';
334 if (length($boring)) {
335 &file("$linkto-b.html",'non',
336 "<html><head><title>$gProject $gBug report logs - ".
337 "$short, boring messages</title>\n".
338 "<link rev=\"made\" href=\"mailto:$gMaintainerEmail)\">\n".
339 "</head>$gHTMLStart<h1>$gProject $gBugreport logs -".
340 "\n <A href=\"../$linkto.html\">$short</A>,".
341 " boring messages</h1>\n$boring\n<hr>\n".
342 $tail_html."</body></html>\n");
344 &file("$linkto.html",'non',
345 "<html><head><title>$gProject $gBug report logs - ".
347 "<link rev=\"made\" href=\"mailto:$gMaintainerEmail\">\n".
348 "</head>$gHTMLStart<h1>$gProject $gBug report logs - $short<br>\n".
349 &sani($data->{subject})."</h1>".
350 "$descriptivehead\n".
353 $tail_html."</body></html>\n");
359 s/([^<>()]+) \(([^()<>]+)\)/$2 \<$1\>/;
363 $email= s/ *\<[^<>()]+\>$//g ? $& : '';
364 $_= "$1 $_" if s/ (\S+)$//;
370 return $maintencoded{$_[0]} if defined($maintencoded{$_[0]});
372 local ($todo,$encoded)= ($input);
373 while ($todo =~ m/\W/) {
374 $encoded.=$`.sprintf("-%02x_",unpack("C",$&));
378 $encoded =~ s/-2e_/\./g;
379 $encoded =~ s/^([^,]+)-20_-3c_(.*)-40_(.*)-3e_/$1,$2,$3,/;
380 $encoded =~ s/^(.*)-40_(.*)-20_-28_([^,]+)-29_$/,$1,$2,$3/;
381 $encoded =~ s/-20_/_/g;
382 $encoded =~ s/-([^_]+)_-/-$1/g;
383 $maintencoded{$input}= $encoded;
386 for $tmaint (keys %countpermaint) {
388 $after=$before=$sort2d=$sort2s=$sort1d=$sort1s='';
389 $after= "$&$after" if s/\s*\<[^<>()]+\>\s*$//;
390 $after= "$&$after" if s/\s*\)\s*$//;
391 $after= "$&$after" if s/\s*,.*$//;
392 $before.= $& if s/^.*\(\s*//;
393 $sort2d= $& if s/\S+$//;
395 while (s/^([^()<>]+)\. */$1 /) { };
396 s/\s+$//; y/A-Za-z/a-zA-Z/; $sort1s= $_;
397 $sort2s= $sort2d; $sort2s =~ y/A-Za-z/a-zA-Z/;
398 $maintsort{$tmaint}= $sort2s.' '.$sort1s.' '.$before.$sort1d.$sort2d.$after;
399 $maintdisplay{$tmaint}=
400 &sani($before).'<strong>'.&sani($sort1d.$sort2d).'</strong>'.&sani($after);
405 return $displayshowseverities{$sv}.' - '.$displayshowpendings{$pt};
408 sub makeindex ($$$) {
409 my ($varprefix,$varsuffix,$tkey) = @_;
410 my ($pending,$severity,$anydone,$text);
413 for $pending (qw(pending forwarded done)) {
414 for $severity (@showseverities) {
415 $estr= "\$value= \\${varprefix}${pending}${severity}${varsuffix}; 1;";
418 or die "eval get \$${varprefix}${pending}${severity} failed: $@";
419 #print STDERR ">$$value<\n";
420 next unless length($$value);
421 $text.= "<hr>\n<h2>".&heading($pending,$severity).":</h2>\n".
422 "(List of <A href=\"../si/$pending$severity.html\">all".
423 " such $gBugs</A> is available.)\n<ul>\n".
426 $anydone=1 if $pending eq 'done';
429 $text.= $expirynote_html if $anydone;
433 &file("ix/full.html",'def',
435 makeindex('$index',"",'').
437 $tail_html."</body><html>\n");
439 &file("ju/junk.html",'non',
441 "<hr>\n<h2>Junk (messages without a specific $gBug report number):</h2>\n".
442 "(\`this week' is everything since last Wednesday.)\n<ul>\n".
445 $tail_html."</body><html>\n");
447 $nobugs_html= "No reports are currently in this state.";
448 $who_html= $gProject;
449 $owner_addr= $gMaintainerEmail;
450 $otherindex_html= "For other kinds of index or for other information about
451 $gProject and the $gBug system, see the <A HREF=\"../../\">$gBug system top-level
452 contents WWW page</A>.
456 for $pending (qw(pending forwarded done)) {
457 for $severity (@showseverities) {
458 eval "\$value= \\\$iiindex${pending}${severity}; 1;"
459 or die "eval get \$iiindex${pendingtype}${severity} failed: $@";
460 $value= \$nobugs_html if !length($$value);
461 $headstring= &heading($pending,$severity);
462 &file("si/$pending$severity.html",'ref',
463 "<html><head><title>$who_html $gBug reports: $headstring</title>\n".
464 "<link rev=\"made\" href=\"mailto:".&sani($owner_addr)."\">\n".
465 "</head>$gHTMLStart<h1>$who_html $gBug reports: $headstring</h1>\n".
467 ($pending eq 'done' ? "<P>\n$expirynote_html" : '').
471 $tail_html."</body></html>\n");
475 sub individualindexes ($\@&\%&&$$$$$&&) {
476 my ($filename,$keysref,$getfilenameref,$countref,$getdisplayref,
477 $getsimpledisplayref,$what,$caveat,$whatplural,$abbrev,$ihead,
478 $getxinforef,$getxindexref) = @_;
479 my ($itext,$i,$tkey,$sani,$count,$tfilename,$refto,$backnext,$xitext,$bugbugs);
481 for ($i=0; $i<=$#$keysref; $i++) {
482 $tkey= $$keysref[$i];
483 $tfilename= &$getfilenameref($tkey);
484 $sani= &$getsimpledisplayref($tkey);
485 $count= $$countref{$tkey};
486 $count= $count >= 1 ? "$count" : "no";
487 $bugbugs= $count == 1 ? "$gBug" : "$gBugs";
488 $xitext= &$getxindexref($tkey);
489 $xitext= length($xitext) ? "$count $bugbugs; $xitext"
490 : "$count outstanding $bugbugs";
491 $itext .= "<li><A href=\"../$tfilename\">".&$getdisplayref($tkey)."</A>"."\n".
495 $refto= $$keysref[$i-1];
496 $xitext= &$getxindexref($refto);
497 $xitext= " ($xitext)" if length($xitext);
498 $backnext .= "<br>\nPrevious $what in list, <A href=\"../".
499 &$getfilenameref($refto)."\">".&$getdisplayref($refto)."</A>".
503 $refto= $$keysref[$i+1];
504 $xitext= &$getxindexref($refto);
505 $xitext= " ($xitext)" if length($xitext);
506 $backnext .= "<br>\nNext $what in list, <A href=\"../".
507 &$getfilenameref($refto)."\">".&$getdisplayref($refto)."</A>".
510 &file($tfilename,'ref',
511 "<html><head><title>$gProject $gBug reports: $what $sani</title>\n".
512 "<link rev=\"made\" href=\"mailto:$gMaintainerEmail\">\n".
513 "</head>$gHTMLStart<h1>$gProject $gBug reports: $what $sani</h1>\n".
514 &$getxinforef($tkey).
516 "See the <A href=\"../$filename\">listing of $whatplural</A>.\n".
518 &makeindex("\$per${abbrev}","{\$tkey}",$tkey).
520 $tail_html."</body></html>\n");
522 &file($filename,'non',
527 $tail_html."</body></html>\n");
530 @maintainers= sort { $maintsort{$a} cmp $maintsort{$b}; } keys %countpermaint;
531 individualindexes('ix/maintainers.html',
533 sub { 'ma/l'.&maintencoded($_[0]).'.html'; },
535 sub { $maintdisplay{$_[0]}; },
536 sub { &sani($_[0]); },
538 "Note that there may be other reports filed under different
539 variations on the maintainer\'s name and email address.<P>",
546 @packages= sort keys %countperpack;
547 individualindexes('ix/packages.html',
549 sub { length($_[0]) ? "pa/l$_[0].html" : 'pa/none.html'; },
551 sub { length($_[0]) ? $_[0] : 'not specified'; },
552 sub { &sani(length($_[0]) ? $_[0] : 'not specified'); },
554 "Note that with multi-binary packages there may be other
555 reports filed under the different binary package names.<P>",
560 return unless defined($maintainer{$_[0]});
561 $tmaint= $maintainer{$_[0]};
562 return "Maintainer for $_[0] is <A href=\"../ma/l".
563 &maintencoded($tmaint).
564 ".html\">".&sani($tmaint)."</A>.\n<p>\n";
567 return unless defined($maintainer{$_[0]});
568 $tmaint= $maintainer{$_[0]};
569 return "<A href=\"../ma/l".
570 &maintencoded($tmaint).
571 ".html\">".&sani($tmaint)."</A>";
574 &file('ix/summary.html','non',
579 $tail_html."</body></html>\n");
582 for $k (map {$_->[0] }
583 sort { $a->[2] cmp $b->[2] || $a->[1] <=> $b->[1] }
584 map { [$_, split(' ',$_,2)] } keys %sient)
585 { $bypackageindex.= $sient{$k}; }
586 &file('ix/psummary.html','non',
588 "<hr><pre>\n$shorthead\n".
591 $tail_html."</body></html>\n");
593 open(P,"$gPseudoDescFile") ||
594 die "$gPseudoDescFile: $!";
595 $ppd=''; while(<P>) { s/\s*\n$//; $ppd.= &sani($_)."\n"; } close(P);
596 &file('ix/pseudopackages.html','non',
600 $tail_html."</body></html>\n");
602 $_= $gHTMLStamp; s/SUBSTITUTE_DTIME/$dtime/o;
604 &file('ix/zstamp.html','non',$_."</body></html>\n");
606 sub notimestamp ($) {
608 s/\<\!\-\-timestamp\-\-\>\n.*\n\<\!\-\-\/timestamp\-\-\>\n//;
613 local ($name,$ii,$file)= @_;
615 $cmppath= "$wwwbase/$name".($ii eq 'ref' ? '.ref' : '');
616 if (open(ORIG,"$cmppath")) {
617 undef $/; $orig= <ORIG>; $/= "\n";
619 if (¬imestamp($orig) eq ¬imestamp($file)) {
620 print "preserve $name\n";
623 defined($c= open(P,"-|")) or die "pipe/fork for diff: $!";
625 open(Q,"|diff -e $cmppath -") or die "pipe/fork II for diff: $!\n";
626 print Q $file or die "write orig to diff: $!\n";
627 close(Q); $?==0 || $?==256 or die "diff gave $?\n";
630 undef $/; $difftxt= <P>; $/= "\n";
631 close(P); $?==0 || $?==256 or die "diff fork gave $?\n";
633 print "preserve $name\n";
636 $v= (split(/\n/,$difftxt));
637 print "diff $v $ii $name\n${difftxt}thatdiff $name\n"
638 or die "stdout (diff): $!";
642 $v= (split(/\n/,$file));
643 print "file $v $ii $name\n${file}thatfile $name\n" or die "stdout: $!";
647 print "preserve $_[0]\n";
652 while ($u= $cleanups[$#cleanups]) { &$u; }