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