]> git.donarmstrong.com Git - debbugs.git/blob - scripts/db2html.in
[project @ 2000-10-10 20:10:34 by joy]
[debbugs.git] / scripts / db2html.in
1 #!/usr/bin/perl
2 # $Id: db2html.in,v 1.6 2000/10/07 17:27:13 joy Exp $
3 # usage: db2html [-diff] [-stampfile=<stampfile>] [-lastrun=<days>] <wwwbase>
4
5 #load the necessary libraries/configuration
6 require('/etc/debbugs/config');
7 require('/etc/debbugs/text');
8 require('/usr/lib/debbugs/errorlib');
9 $ENV{'PATH'} = '/usr/lib/debbugs:'.$ENV{'PATH'};
10
11 #set current working directory
12 chdir("$gSpoolDir") || die "chdir spool: $!\n";
13
14 #setup variables
15 $diff = 0;
16 $stampfile = 'stamp.html';
17 $tail_html = $gHTMLTail; 
18 $expirynote_html = $gHTMLExpireNote;
19 $shorthead = ' Ref   * Package    Keywords/Subject                    Submitter';
20 $shortindex = ''; 
21 $amonths = -1;
22 $indexunmatched = '';
23 %displayshowpendings = ('pending','outstanding',
24                        'done','resolved',
25                        'forwarded','forwarded to upstream software authors');
26
27 #set timestamp for html files
28 chop($dtime=`date -u '+%H:%M:%S GMT %a %d %h'`); $? and die $?;
29 $tail_html =~ s/SUBSTITUTE_DTIME/$dtime/;
30
31 #check for commandline switches
32 while (@ARGV && $ARGV[0] =~ m/^-/) 
33 {       if ($ARGV[0] eq '-diff') { $diff=1; }
34     elsif ($ARGV[0] =~ m/^-lastrun\=([0-9.]+)$/) { $lastrun= $1; undef $stampfile; }
35     elsif ($ARGV[0] =~ m/^-full$/) { undef $lastrun; undef $stampfile; }
36     elsif ($ARGV[0] =~ m/^-stampfile\=(\S+)$/) { $stampfile= $1; }
37     else { &quit("bad usage"); }
38     shift;
39 }
40
41 #check for remaing argument, only one...
42 @ARGV==1 or die;
43 $wwwbase= shift(@ARGV);
44
45 #get starting time
46 defined($startdate= time) || &quit("failed to get time: $!");
47
48 $|=1;
49
50 #if stamp file was given, 
51 if (defined($stampfile)) 
52 {       if (open(X,"< $stampfile")) 
53         {       $lastrun= -M X;
54         close(X);
55         printf "progress last run %.7f days\n",$lastrun;
56     } else { print "progress stamp file $stampfile: $! - full\n"; }
57 }
58
59 #only process file if greater than last run...
60 if (defined($lastrun) && -M "db" > $lastrun) 
61 {       $_= $gHTMLStamp;
62     s/SUBSTITUTE_DTIME/$dtime/o;
63     s/\<\!\-\-updateupdate\-\-\>.*\<\!\-\-\/updateupdate\-\-\>/check/;
64     &file('ix/zstamp.html','non',$_."</body></html>\n");
65         print "noremoves";
66 #    print "db2html: no changes since last run\n";
67     exit 0;
68 }
69
70 #parse maintainer file
71 open(MM,"$gMaintainerFile") || &quit("open $gMaintainerFile: $!");
72 while(<MM>) 
73 {       m/^(\S+)\s+(\S.*\S)\s*$/ || &quit("$gMaintainerFile: \`$_'");
74     ($a,$b)=($1,$2);
75     $a =~ y/A-Z/a-z/;
76     $maintainer{$a}= $b;
77 }
78 close(MM);
79
80 #load all database files
81 opendir(D,'db') || &quit("opendir db: $!");
82 @files= sort { $a <=> $b } readdir(D);
83 closedir(D);
84
85 for $pending (qw(pending done forwarded)) 
86 {       for $severity (@showseverities) 
87         {       eval "\$index${pending}${severity}= \$iiindex${pending}${severity}= ''; 1;"
88             or &quit("reset \$index${pending}${severity}: $@");
89     }
90 }
91
92 for $f (@files) 
93 {       next unless $f =~ m/^(-?\d+)\.log$/;
94     $ref= $1;
95         #((print STDERR "$ref\n"),
96         #next
97         #)
98         # unless $ref =~ m/^-/ || $ref =~ m/^124/;
99     &filelock("lock/$ref");
100     $preserveonly= defined($lastrun) && -M "db/$ref.log" > $lastrun;
101     if ($ref =~ m/^-\d$/) 
102         {       $week= $ref eq '-1' ? 'this week' :
103                $ref eq '-2' ? 'last week' :
104                $ref eq '-3' ? 'two weeks ago' :
105                               ($ref-1)." weeks ago";
106         $linkto= "ju/unmatched$ref";
107         $short= "junk, $week";
108         $descriptivehead=
109             "This includes messages sent to <code>done\@$gEmailDomain</code>\n".
110             "which did not have a $gBug reference number in the Subject line\n".
111             "or which contained an\n".
112             "unknown or out of date $gBug report number (these cause a warning\n".
113             "to be sent to the sender) and details about the messages\n".
114             "sent to <code>request@$gEmailDomain</code> (all of which".
115             "produce replies).\n";
116         $indexlink= "Messages not matched to a specific $gBug report - $week";
117         $s_subject= '';
118         $indexentry= '';
119         undef $tpack;
120         undef $tmaint;
121         undef $iiref;
122         $tpackfile= "pnone.html";
123         $indexpart= 'unmatched';
124     } else 
125         {       open(S,"db/$ref.status") || &quit("open db/$ref.status: $!");
126         chomp($s_originator= <S>);
127         chomp($s_date= <S>);
128         chomp($s_subject= <S>);
129         chomp($s_msgid= <S>);
130         chomp($s_package= <S>);
131         chomp($s_keywords= <S>);
132         chomp($s_done= <S>);
133         chomp($s_forwarded= <S>);
134         chomp($s_mergedwith= <S>);
135         chomp($s_severity= <S>);
136         $_= $s_package; y/A-Z/a-z/; $_= $` if m/[^-+._a-z0-9()]/;
137         $tpack= $_;
138         if ($s_severity eq '' || $s_severity eq 'normal') 
139                 {       $showseverity= '';
140             $addseverity= $gDefaultSeverity;
141         } elsif (grep($s_severity eq $_, @strongseverities)) 
142                 {       $showseverity= "<strong>Severity: $s_severity</strong>;\n";
143             $addseverity= $s_severity;
144         } else 
145                 {       $showseverity= "Severity: <em>$s_severity</em>;\n";
146             $addseverity= $s_severity;
147         }
148         $days= int(($startdate - $s_date)/86400); close(S);
149         $indexlink= "#$ref: ".&sani($s_subject);
150         $indexentry= '';
151         $packfile= length($tpack) ? "pa/l$tpack.html" : "pa/none.html";
152         $indexentry .= "Package: <A href=\"../$packfile\"><strong>".
153                         &sani($s_package)."</strong></A>;\n"
154             if length($s_package);
155         $indexentry .= $showseverity;
156         $indexentry .= "Reported by: ".&sani($s_originator);
157         $indexentry .= ";\nKeywords: ".&sani($s_keywords)
158             if length($s_keywords);
159         $linkto= $ref; $linkto =~ s,^..,$&/$&,;
160         @merged= split(/ /,$s_mergedwith);
161         if (@merged) 
162                 {       $mseparator= ";\nmerged with ";
163             for $m (@merged) 
164                         {       $mfile= $m; $mfile =~ s,^..,$&/$&,;
165                 $indexentry .= $mseparator."<A href=\"../$mfile.html\">#$m</A>";
166                 $mseparator= ",\n";
167             }
168         }
169         $daysold=$submitted='';
170         if (length($s_done)) 
171                 {       $indexentry .= ";\n<strong>Done:</strong> ".&sani($s_done);
172             $indexpart= "done$addseverity";
173         } elsif (length($s_forwarded)) 
174                 {       $indexentry .= ";\n<strong>Forwarded</strong> to ".&sani($s_forwarded);
175             $indexpart= "forwarded$addseverity";
176         } else 
177                 {       $cmonths= int($days/30);
178             if ($cmonths != $amonths) 
179                         {       $msg= $cmonths == 0 ? "Submitted in the last month" :
180                         $cmonths == 1 ? "Over one month old" :
181                         $cmonths == 2 ? "Over two months old - attention is required" :
182                         "OVER $cmonths MONTHS OLD - ATTENTION IS REQUIRED";
183                 $shortindex .= "</pre><h2>$msg:</h2><pre>\n$shorthead\n";
184                 $amonths= $cmonths;
185             }
186             $pad= 6-length(sprintf("%d",$f));
187             $thissient=
188                 ($pad>0 ? ' 'x$pad : '').
189                 sprintf("<A href=\"../%s.html\">%d</A>",$linkto,$ref).
190                 &sani(sprintf(" %-1.1s %-10.10s %-35.35s %-.25s\n",
191                                                 $s_severity,
192                         $s_package,
193                         (length($s_keywords) ? $s_keywords.'/' : '').
194                         $s_subject, $s_originator));
195             $shortindex.= $thissient;
196             $sient{"$ref $s_package"}= $thissient;
197             if ($days >= 7) 
198                         {       $font= $days <= 30 ? '' :
199                         $days <= 60 ? 'em' :
200                     'strong';
201                 $efont= length($font) ? "</$font>" : '';
202                 $font= length($font) ? "<$font>" : '';
203                 $daysold= "; $font$days days old$efont";
204             }
205             if ($preserveonly) { $submitted= 'THIS IS A BUG IN THE BUG PROCESSOR'; } 
206                         else 
207                         {       $submitted= `TZ=GMT LANG=C \\
208                              date -d '1 Jan 1970 00:00:00 + $s_date seconds' \\
209                              '+ %a, %d %b %Y %T %Z'`;
210                 $? and die $?;
211             }
212             $submitted =~ s/\n$//; $submitted =~ s/, 0/, /g;
213             $submitted= "; dated $submitted";
214             $indexpart= "pending$addseverity";
215         }
216         $iiref= $ref;
217         $short= $ref; $short =~ s/^\d+/#$&/;
218         $tmaint= defined($maintainer{$tpack}) ? $maintainer{$tpack} : '(unknown)';
219         $qpackage= &sani($_);
220         $descriptivehead= $indexentry.$submitted.";\nMaintainer for $qpackage is\n".
221             '<A href="../ma/l'.&maintencoded($tmaint).'.html">'.&sani($tmaint).'</A>.';
222         $indexentry .= $daysold;
223         $indexentry .= ".";
224     }
225     $indexadd='';
226     $indexadd .= "<!--iid $iiref-->" if defined($iiref);
227     $indexadd .= "<li><A href=\"../$linkto.html\">".$indexlink."</A>";
228     $indexadd .=  "<br>\n".$indexentry if length($indexentry);
229     $indexadd .= "<!--/iid-->" if defined($iiref);
230     $indexadd .= "\n";
231     $estr= "\$index$indexpart = \$indexadd.\$index$indexpart; 1;";
232     eval($estr) || &quit("eval add to \$index$indexpart ($estr) failed: $@");
233         #print STDERR ">$estr|$indexadd<\n";
234     $indexadd= "<!--ii $iiref-->\n" if defined($iiref);
235     eval("\$iiindex$indexpart = \$indexadd.\$iiindex$indexpart; 1;") ||
236         &quit("eval add to \$iiindex$indexpart failed: $@");
237     if (defined($tmaint)) 
238         {       $countpermaint{$tmaint} += length($s_done) ? 0 : length($s_forwarded) ? 0 : 1;
239         eval("\$permaint${indexpart}{\$tmaint} .= \$indexadd; 1;") ||
240             &quit("eval add to \$permaint${indexpart}{\$tmaint} failed: $@");
241     }
242     if (defined($tpack)) 
243         {       $countperpack{$tpack} += length($s_done) ? 0 : length($s_forwarded) ? 0 : 1;
244         eval("\$perpack${indexpart}{\$tpack} .= \$indexadd; 1;") ||
245             &quit("eval add to \$perpack${indexpart}{\$tpack} failed: $@");
246     }
247     if ($preserveonly) { &preserve("$linkto.html"); &preserve("$linkto-b.html"); &unfilelock; next; }
248     open(L,"db/$ref.log") || &quit("open db/$ref.log: $!");
249     $log='';
250     $boring=''; $xmessage= 0;
251     $normstate= 'kill-init';
252     $suppressnext= 0;
253     while(<L>) {
254         if (m/^\07$/) {
255             $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
256                 &quit("$ref ^G in state $normstate");
257             $normstate= 'incoming-recv';
258         } elsif (m/^\01$/) {
259             $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
260                 &quit("$ref ^A in state $normstate");
261             $normstate= 'autocheck';
262         } elsif (m/^\02$/) {
263             $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
264                 &quit("$ref ^B in state $normstate");
265             $normstate= 'recips';
266         } elsif (m/^\03$/) {
267             $normstate eq 'go' || $normstate eq 'go-nox' || $normstate eq 'html' ||
268                 &quit("$ref ^C in state $normstate");
269             $this .= "</pre>\n" if $normstate eq 'go' || $normstate eq 'go-nox';
270             if ($normstate eq 'html') {
271                 $xmessage++;
272                 $this .= "  <em><A href=\"../$linkto-b.html#m$xmessage\">Full text</A>".
273                          " available.</em>";
274             }
275             if ($suppressnext && $normstate ne 'html') {
276                 $ntis= $this; $ntis =~ s:\<pre\>:</A><pre>:i;
277                 $boring .= "<hr><A name=\"m$xmessage\">\n$ntis\n";
278             } else {
279                 $log = $this. "<hr>\n". $log;
280             }
281             $suppressnext= $normstate eq 'html';
282             $normstate= 'kill-end';
283         } elsif (m/^\05$/) {
284             $normstate eq 'kill-body' || &quit("^E in state $normstate");
285             $this .= "<pre>\n";
286             $normstate= 'go';
287         } elsif (m/^\06$/) {
288             $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
289                 &quit("$ref ^F in state $normstate");
290             $normstate= 'html'; $this= '';
291         } elsif ($normstate eq 'incoming-recv') {
292             $pl= $_; $pl =~ s/\n+$//;
293             m/^Received: \(at (\S+)\) by (\S+)\;/ ||
294                 &quit("bad line \`$pl' in state incoming-recv");
295             $this = "<h2>Message received at ".&sani("$1\@$2").":</h2><br>\n".
296                     "<pre>\n".
297                     "$_";
298             $normstate= 'go';
299         } elsif ($normstate eq 'html') {
300             $this .= $_;
301         } elsif ($normstate eq 'go') {
302             $this .= &sani($_);
303         } elsif ($normstate eq 'go-nox') {
304             next if !s/^X//;
305             $this .= &sani($_);
306         } elsif ($normstate eq 'recips') {
307             if (m/^-t$/) {
308                 $this = "<h2>Message sent:</h2><br>\n";
309             } else {
310                 s/\04/, /g; s/\n$//;
311                 $this = "<h2>Message sent to ".&sani($_).":</h2><br>\n";
312             }
313             $normstate= 'kill-body';
314         } elsif ($normstate eq 'autocheck') {
315             next if !m/^X-Debian-Bugs(-\w+)?: This is an autoforward from (\S+)/;
316             $normstate= 'autowait';
317             $this = "<h2>Message received at $2:</h2><br>\n";
318         } elsif ($normstate eq 'autowait') {
319             next if !m/^$/;
320             $normstate= 'go-nox';
321             $this .= "<pre>\n";
322         } else {
323             &quit("$ref state $normstate line \`$_'");
324         }
325     }
326     &quit("$ref state $normstate at end") unless $normstate eq 'kill-end';
327     close(L);
328     if (length($boring)) {
329         &file("$linkto-b.html",'non',
330               "<html><head><title>$gProject $gBug report logs - ".
331               "$short, boring messages</title>\n".
332               "<link rev=\"made\" href=\"mailto:$gMaintainerEmail)\">\n".
333               "</head>$gHTMLStart<h1>$gProject $gBugreport logs -".
334               "\n <A href=\"../$linkto.html\">$short</A>,".
335               " boring messages</h1>\n$boring\n<hr>\n".
336               $tail_html."</body></html>\n");
337     }
338     &file("$linkto.html",'non',
339           "<html><head><title>$gProject $gBug report logs - ".
340           "$short</title>\n".
341           "<link rev=\"made\" href=\"mailto:$gMaintainerEmail\">\n".
342           "</head>$gHTMLStart<h1>$gProject $gBug report logs -  $short<br>\n".
343           &sani($s_subject)."</h1>".
344           "$descriptivehead\n".
345           "\n<hr>\n".
346           $log.
347           $tail_html."</body></html>\n");
348     &unfilelock;
349 }
350
351 sub maintsort {
352     $_= $_[0];
353     s/([^<>()]+) \(([^()<>]+)\)/$2 \<$1\>/;
354     
355     s/\s+/ /g;
356     s/^\s*//;
357     $email= s/ *\<[^<>()]+\>$//g ? $& : '';
358     $_= "$1 $_" if s/ (\S+)$//;
359     $_.= $email;
360     $_;
361 }
362
363 sub maintencoded {
364     return $maintencoded{$_[0]} if defined($maintencoded{$_[0]});
365     local ($input)= @_;
366     local ($todo,$encoded)= ($input);
367     while ($todo =~ m/\W/) {
368         $encoded.=$`.sprintf("-%02x_",unpack("C",$&));
369         $todo= $';
370     }
371     $encoded.= $todo;
372     $encoded =~ s/-2e_/\./g;
373     $encoded =~ s/^([^,]+)-20_-3c_(.*)-40_(.*)-3e_/$1,$2,$3,/;
374     $encoded =~ s/^(.*)-40_(.*)-20_-28_([^,]+)-29_$/,$1,$2,$3/;
375     $encoded =~ s/-20_/_/g;
376     $encoded =~ s/-([^_]+)_-/-$1/g;
377     $maintencoded{$input}= $encoded;
378 }
379
380 for $tmaint (keys %countpermaint) {
381     $_= $tmaint;
382     $after=$before=$sort2d=$sort2s=$sort1d=$sort1s='';
383     $after= "$&$after" if s/\s*\<[^<>()]+\>\s*$//;
384     $after= "$&$after" if s/\s*\)\s*$//;
385     $after= "$&$after" if s/\s*,.*$//;
386     $before.= $& if s/^.*\(\s*//;
387     $sort2d= $& if s/\S+$//;
388     $sort1d= $_;
389     while (s/^([^()<>]+)\. */$1 /) { };
390     s/\s+$//; y/A-Za-z/a-zA-Z/; $sort1s= $_;
391     $sort2s= $sort2d; $sort2s =~ y/A-Za-z/a-zA-Z/;
392     $maintsort{$tmaint}= $sort2s.' '.$sort1s.' '.$before.$sort1d.$sort2d.$after;
393     $maintdisplay{$tmaint}=
394         &sani($before).'<strong>'.&sani($sort1d.$sort2d).'</strong>'.&sani($after);
395 }
396
397 sub heading ($$) {
398     my ($pt,$sv) = @_;
399     return $displayshowseverities{$sv}.' - '.$displayshowpendings{$pt};
400 }
401
402 sub makeindex ($$$) {
403     my ($varprefix,$varsuffix,$tkey) = @_;
404     my ($pending,$severity,$anydone,$text);
405     $anydone= 0;
406     $text= '';
407     for $pending (qw(pending forwarded done)) {
408         for $severity (@showseverities) {
409             $estr= "\$value= \\${varprefix}${pending}${severity}${varsuffix}; 1;";
410 #print STDERR $estr;
411             eval $estr
412                 or &quit("eval get \$${varprefix}${pending}${severity} failed: $@");
413 #print STDERR ">$$value<\n";
414             next unless length($$value);
415             $text.= "<hr>\n<h2>".&heading($pending,$severity).":</h2>\n".
416                     "(List of <A href=\"../si/$pending$severity.html\">all".
417                     " such $gBugs</A> is available.)\n<ul>\n".
418                     $$value.
419                     "</ul>\n";
420             $anydone=1 if $pending eq 'done';
421         }
422     }
423     $text.= $expirynote_html if $anydone;
424     return $text;
425 }        
426
427 &file("ix/full.html",'def',
428       $gFullIndex.
429       makeindex('$index',"",'').
430       "<hr>\n".
431       $tail_html."</body><html>\n");
432
433 &file("ju/junk.html",'non',
434       $gJunkIndex.
435       "<hr>\n<h2>Junk (messages without a specific $gBug report number):</h2>\n".
436       "(\`this week' is everything since last Wednesday.)\n<ul>\n".
437       $indexunmatched.
438       "</ul><hr>\n".
439       $tail_html."</body><html>\n");
440
441 $nobugs_html= "No reports are currently in this state.";
442 $who_html= $gProject;
443 $owner_addr= $gMaintainerEmail;
444 $otherindex_html= "For other kinds of index or for other information about
445 $gProject and the $gBug system, see the <A HREF\"../../\">$gBug system top-level
446 contents WWW page</A>.
447
448 ";
449
450 for $pending (qw(pending forwarded done)) {
451     for $severity (@showseverities) {
452         eval "\$value= \\\$iiindex${pending}${severity}; 1;"
453             or &quit("eval get \$iiindex${pendingtype}${severity} failed: $@");
454         $value= \$nobugs_html if !length($$value);
455         $headstring= &heading($pending,$severity);
456         &file("si/$pending$severity.html",'ref',
457               "<html><head><title>$who_html $gBug reports: $headstring</title>\n".
458               "<link rev=\"made\" href=\"mailto:".&sani($owner_addr)."\">\n".
459               "</head>$gHTMLStart<h1>$who_html $gBug reports: $headstring</h1>\n".
460               $otherindex_html.
461               ($pending eq 'done' ? "<P>\n$expirynote_html" : '').
462               "<hr>\n".
463               $$value.
464               "<hr>\n".
465               $tail_html."</body></html>\n");
466     }
467 }
468
469 sub individualindexes ($\@&\%&&$$$$$&&) {
470     my ($filename,$keysref,$getfilenameref,$countref,$getdisplayref,
471         $getsimpledisplayref,$what,$caveat,$whatplural,$abbrev,$ihead,
472         $getxinforef,$getxindexref) = @_;
473     my ($itext,$i,$tkey,$sani,$count,$tfilename,$refto,$backnext,$xitext,$bugbugs);
474     $itext='';
475     for ($i=0; $i<=$#$keysref; $i++) {
476         $tkey= $$keysref[$i];
477         $tfilename= &$getfilenameref($tkey);
478         $sani= &$getsimpledisplayref($tkey);
479         $count= $$countref{$tkey};
480         $count= $count >= 1 ? "$count" : "no";
481         $bugbugs= $count == 1 ? "$gBug" : "$gBugs";
482         $xitext= &$getxindexref($tkey);
483         $xitext= length($xitext) ? "$count $bugbugs; $xitext"
484                                  : "$count outstanding $bugbugs";
485         $itext .= "<li><A href=\"../$tfilename\">".&$getdisplayref($tkey)."</A>"."\n".
486                   "  ($xitext)\n";
487         $backnext= '';
488         if ($i>0) {
489             $refto= $$keysref[$i-1];
490             $xitext= &$getxindexref($refto);
491             $xitext= " ($xitext)" if length($xitext);
492             $backnext .= "<br>\nPrevious $what in list, <A href=\"../".
493                          &$getfilenameref($refto)."\">".&$getdisplayref($refto)."</A>".
494                          "$xitext\n";
495         }
496         if ($i<$#$keysref) {
497             $refto= $$keysref[$i+1];
498             $xitext= &$getxindexref($refto);
499             $xitext= " ($xitext)" if length($xitext);
500             $backnext .= "<br>\nNext $what in list, <A href=\"../".
501                          &$getfilenameref($refto)."\">".&$getdisplayref($refto)."</A>".
502                          "$xitext\n";
503         }
504         &file($tfilename,'ref',
505               "<html><head><title>$gProject $gBug reports: $what $sani</title>\n".
506               "<link rev=\"made\" href=\"mailto:$gMaintainerEmail\">\n".
507               "</head>$gHTMLStart<h1>$gProject $gBug reports: $what $sani</h1>\n".
508               &$getxinforef($tkey).
509               $caveat.
510               "See the <A href=\"../$filename\">listing of $whatplural</A>.\n".
511               $backnext.
512               &makeindex("\$per${abbrev}","{\$tkey}",$tkey).
513               "<hr>\n".
514               $tail_html."</body></html>\n");
515     }
516     &file($filename,'non',
517           $ihead.
518           "<hr><ul>\n".
519           $itext.
520           "</ul><hr>\n".
521           $tail_html."</body></html>\n");
522 }
523
524 @maintainers= sort { $maintsort{$a} cmp $maintsort{$b}; } keys %countpermaint;
525 individualindexes('ix/maintainers.html',
526                   @maintainers,
527                   sub { 'ma/l'.&maintencoded($_[0]).'.html'; },
528                   %countpermaint,
529                   sub { $maintdisplay{$_[0]}; },
530                   sub { &sani($_[0]); },
531                   'maintainer',
532                   "Note that there may be other reports filed under different
533                                   variations on the maintainer\'s name and email address.<P>",
534                   'maintainers',
535                   'maint',
536                   $gMaintIndex,
537                   sub { return ''; },
538                   sub { return ''; });
539
540 @packages= sort keys %countperpack;
541 individualindexes('ix/packages.html',
542                   @packages,
543                   sub { length($_[0]) ? "pa/l$_[0].html" : 'pa/none.html'; },
544                   %countperpack,
545                   sub { length($_[0]) ? $_[0] : 'not specified'; },
546                   sub { &sani(length($_[0]) ? $_[0] : 'not specified'); },
547                   'package',
548                   "Note that with multi-binary packages there may be other
549                                   reports filed under the different binary package names.<P>",
550                   'packages',
551                   'pack',
552                   $gPackageIndex,
553                   sub {
554                       return unless defined($maintainer{$_[0]});
555                       $tmaint= $maintainer{$_[0]};
556                       return "Maintainer for $_[0] is <A href=\"../ma/l".
557                              &maintencoded($tmaint).
558                              ".html\">".&sani($tmaint)."</A>.\n<p>\n";
559                   },
560                   sub {
561                       return unless defined($maintainer{$_[0]});
562                       $tmaint= $maintainer{$_[0]};
563                       return "<A href=\"../ma/l".
564                              &maintencoded($tmaint).
565                              ".html\">".&sani($tmaint)."</A>";
566                   });
567
568 &file('ix/summary.html','non',
569       $gSummaryIndex.
570       "<hr><pre>\n".
571       $shortindex.
572       "</pre><hr>\n".
573       $tail_html."</body></html>\n");
574
575 $bypackageindex='';
576 for $k (map {$_->[0] }
577         sort { $a->[2] cmp $b->[2]  ||  $a->[1] <=> $b->[1] }
578         map { [$_, split(' ',$_,2)] } keys %sient)
579     { $bypackageindex.= $sient{$k}; }
580 &file('ix/psummary.html','non',
581       $gPackageLog.
582       "<hr><pre>\n$shorthead\n".
583       $bypackageindex.
584       "</pre><hr>\n".
585       $tail_html."</body></html>\n");
586
587 open(P,"$gPseudoDescFile") ||
588     &quit("$gPseudoDescFile: $!");
589 $ppd=''; while(<P>) { s/\s*\n$//; $ppd.= &sani($_)."\n"; } close(P);
590 &file('ix/pseudopackages.html','non',
591       $gPseudoIndex.
592       "<hr><pre>\n$ppd".
593       "</pre><hr>\n".
594       $tail_html."</body></html>\n");
595
596 $_= $gHTMLStamp; s/SUBSTITUTE_DTIME/$dtime/o;
597
598 &file('ix/zstamp.html','non',$_."</body></html>\n");
599
600 sub notimestamp ($) {
601     $_= $_[0];
602     s/\<\!\-\-timestamp\-\-\>\n.*\n\<\!\-\-\/timestamp\-\-\>\n//;
603     return $_;
604 }
605
606 sub file {
607     local ($name,$ii,$file)= @_;
608     if ($diff) {
609         $cmppath= "$wwwbase/$name".($ii eq 'ref' ? '.ref' : '');
610         if (open(ORIG,"$cmppath")) {
611             undef $/; $orig= <ORIG>; $/= "\n";
612             close(ORIG);
613             if (&notimestamp($orig) eq &notimestamp($file)) {
614                 print "preserve $name\n";
615                 return;
616             }
617             defined($c= open(P,"-|")) or &quit("pipe/fork for diff: $!");
618             if (!$c) {
619                 open(Q,"|diff -e $cmppath -") or die "pipe/fork II for diff: $!\n";
620                 print Q $file or die "write orig to diff: $!\n";
621                 close(Q); $?==0 || $?==256 or die "diff gave $?\n";
622                 exit($?>>8);
623             }
624             undef $/; $difftxt= <P>; $/= "\n";
625             close(P); $?==0 || $?==256 or die "diff fork gave $?\n";
626             if ($?==0) {
627                 print "preserve $name\n";
628                 return;
629             }
630             $v= (split(/\n/,$difftxt));
631             print "diff $v $ii $name\n${difftxt}thatdiff $name\n"
632                 or &quit("stdout (diff): $!");
633             return;
634         }
635     } 
636     $v= (split(/\n/,$file));
637     print "file $v $ii $name\n${file}thatfile $name\n" or &quit("stdout: $!");
638 }
639
640 sub preserve {
641     print "preserve $_[0]\n";
642 }
643
644 print "end\n";
645
646 while ($u= $cleanups[$#cleanups]) { &$u; }
647 exit 0;