]> git.donarmstrong.com Git - debbugs.git/blob - scripts/db2html
fix plural of {src,bin}packages? in updatesql
[debbugs.git] / scripts / db2html
1 #!/usr/bin/perl
2 # $Id: db2html.in,v 1.22 2004/04/19 10:03:53 cjwatson 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 tzset);
15 $ENV{"TZ"} = 'UTC';
16 tzset();
17
18 #set current working directory
19 chdir("$gSpoolDir") || die "chdir spool: $!\n";
20
21 #setup variables
22 $diff = 0;
23 $stampfile = 'stamp.html';
24 $tail_html = $gHTMLTail; 
25 $expirynote_html = '';
26 $expirynote_html = $gHTMLExpireNote if $gRemoveAge;
27 $shorthead = ' Ref   * Package    Keywords/Subject                    Submitter';
28 $shortindex = ''; 
29 $amonths = -1;
30 $indexunmatched = '';
31 %displayshowpendings = ('pending','outstanding',
32                        'done','resolved',
33                        'forwarded','forwarded to upstream software authors');
34
35 #set timestamp for html files
36 $dtime = strftime "%a, %e %b %Y %T UTC", localtime;
37 $tail_html =~ s/SUBSTITUTE_DTIME/$dtime/;
38
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"; }
46     shift;
47 }
48
49 #check for remaing argument, only one...
50 @ARGV==1 or die;
51 $wwwbase= shift(@ARGV);
52
53 #get starting time
54 defined($startdate= time) || die "failed to get time: $!";
55
56 $|=1;
57
58 #if stamp file was given, 
59 if (defined($stampfile)) 
60 {       if (open(X,"< $stampfile")) 
61         {       $lastrun= -M X;
62         close(X);
63         printf "progress last run %.7f days\n",$lastrun;
64     } else { print "progress stamp file $stampfile: $! - full\n"; }
65 }
66
67 #only process file if greater than last run...
68 if (defined($lastrun) && -M "db-h" > $lastrun) 
69 {       $_= $gHTMLStamp;
70     s/SUBSTITUTE_DTIME/$dtime/o;
71     s/\<\!\-\-updateupdate\-\-\>.*\<\!\-\-\/updateupdate\-\-\>/check/;
72     &file('ix/zstamp.html','non',$_."</body></html>\n");
73         print "noremoves";
74 #    print "db2html: no changes since last run\n";
75     exit 0;
76 }
77
78 #parse maintainer file
79 open(MM,"$gMaintainerFile") || die "open $gMaintainerFile: $!";
80 while(<MM>) 
81 {       m/^(\S+)\s+(\S.*\S)\s*$/ || die "$gMaintainerFile: \`$_'";
82     ($a,$b)=($1,$2);
83     $a =~ y/A-Z/a-z/;
84     $maintainer{$a}= $b;
85 }
86 close(MM);
87
88 #load all database files
89 opendir(D,'db-h') || die "opendir db-h: $!";
90 @dirs = grep(s#^#db-h/#,grep(/^\d+$/,readdir(D)));
91 closedir(D);
92 foreach my $dir (@dirs) {
93     opendir(D,$dir);
94     push @files, grep(/^-?\d+\.log$/,readdir(D));
95     closedir(D);
96 }
97 @files = sort { $a <=> $b } @files;
98
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}: $@";
103     }
104 }
105
106 for $f (@files) 
107 {       next unless $f =~ m/^(-?\d+)\.log$/;
108     $ref= $1;
109         #((print STDERR "$ref\n"),
110         #next
111         #)
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";
122         $descriptivehead=
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}= '';
132         $indexentry= '';
133         undef $tpack;
134         undef $tmaint;
135         undef $iiref;
136         $tpackfile= "pnone.html";
137         $indexpart= 'unmatched';
138     } else 
139         {
140         $data=readbug($ref);
141         $_= $data->{package}; y/A-Z/a-z/; $_= $` if m/[^-+._a-z0-9()]/;
142         $tpack= $_;
143         if ($data->{severity} eq '' || $data->{severity} eq 'normal') 
144                 {       $showseverity= '';
145             $addseverity= $gDefaultSeverity;
146         } elsif (isstrongseverity($data->{severity})) 
147                 {       $showseverity= "<strong>Severity: $data->{severity}</strong>;\n";
148             $addseverity= $data->{severity};
149         } else 
150                 {       $showseverity= "Severity: <em>$data->{severity}</em>;\n";
151             $addseverity= $data->{severity};
152         }
153         $days= int(($startdate - $data->{date})/86400); close(S);
154         $indexlink= "#$ref: ".&sani($data->{subject});
155         $indexentry= '';
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});
168         if (@merged) 
169                 {       $mseparator= ";\nmerged with ";
170             for $m (@merged) 
171                         {       $mfile= $m; $mfile =~ s,^..,$&/$&,;
172                 $indexentry .= $mseparator."<A href=\"../$mfile.html\">#$m</A>";
173                 $mseparator= ",\n";
174             }
175         }
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";
183         } else 
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";
191                 $amonths= $cmonths;
192             }
193             $pad= 6-length(sprintf("%d",$f));
194             $thissient=
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",
198                                                 $data->{severity},
199                         $data->{package},
200                         (length($data->{keywords}) ? $data->{keywords}.'/' : '').
201                         $data->{subject}, $data->{originator}));
202             $shortindex.= $thissient;
203             $sient{"$ref $data->{package}"}= $thissient;
204             if ($days >= 7) 
205                         {       $font= $days <= 30 ? '' :
206                         $days <= 60 ? 'em' :
207                     'strong';
208                 $efont= length($font) ? "</$font>" : '';
209                 $font= length($font) ? "<$font>" : '';
210                 $daysold= "; $font$days days old$efont";
211             }
212             if ($preserveonly) {
213                 $submitted = 'THIS IS A BUG IN THE BUG PROCESSOR';
214             } else {
215                 $submitted = strftime "%a, %e %b %Y %T %Z", localtime($data->{date});
216             }
217             $submitted= "; dated $submitted";
218             $indexpart= "pending$addseverity";
219         }
220         $iiref= $ref;
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;
227         $indexentry .= ".";
228     }
229     $indexadd='';
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);
234     $indexadd .= "\n";
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: $@";
245     }
246     if (defined($tpack)) 
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: $@";
250     }
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: $!";
254     $log='';
255     $boring=''; $xmessage= 0;
256     $normstate= 'kill-init';
257     $suppressnext= 0;
258     while(<L>) {
259         if (m/^\07$/) {
260             $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
261                 die "$ref ^G in state $normstate";
262             $normstate= 'incoming-recv';
263         } elsif (m/^\01$/) {
264             $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
265                 die "$ref ^A in state $normstate";
266             $normstate= 'autocheck';
267         } elsif (m/^\02$/) {
268             $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
269                 die "$ref ^B in state $normstate";
270             $normstate= 'recips';
271         } elsif (m/^\03$/) {
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') {
276                 $xmessage++;
277                 $this .= "  <em><A href=\"../$linkto-b.html#m$xmessage\">Full text</A>".
278                          " available.</em>";
279             }
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";
283             } else {
284                 $log = $this. "<hr>\n". $log;
285             }
286             $suppressnext= $normstate eq 'html';
287             $normstate= 'kill-end';
288         } elsif (m/^\05$/) {
289             $normstate eq 'kill-body' || die "^E in state $normstate";
290             $this .= "<pre>\n";
291             $normstate= 'go';
292         } elsif (m/^\06$/) {
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".
301                     "<pre>\n".
302                     "$_";
303             $normstate= 'go';
304         } elsif ($normstate eq 'html') {
305             $this .= $_;
306         } elsif ($normstate eq 'go') {
307             s/^\030//;
308             $this .= &sani($_);
309         } elsif ($normstate eq 'go-nox') {
310             next if !s/^X//;
311             $this .= &sani($_);
312         } elsif ($normstate eq 'recips') {
313             if (m/^-t$/) {
314                 $this = "<h2>Message sent:</h2><br>\n";
315             } else {
316                 s/\04/, /g; s/\n$//;
317                 $this = "<h2>Message sent to ".&sani($_).":</h2><br>\n";
318             }
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') {
325             next if !m/^$/;
326             $normstate= 'go-nox';
327             $this .= "<pre>\n";
328         } else {
329             die "$ref state $normstate line \`$_'";
330         }
331     }
332     die "$ref state $normstate at end" unless $normstate eq 'kill-end';
333     close(L);
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");
343     }
344     &file("$linkto.html",'non',
345           "<html><head><title>$gProject $gBug report logs - ".
346           "$short</title>\n".
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".
351           "\n<hr>\n".
352           $log.
353           $tail_html."</body></html>\n");
354     &unfilelock;
355 }
356
357 sub maintsort {
358     $_= $_[0];
359     s/([^<>()]+) \(([^()<>]+)\)/$2 \<$1\>/;
360     
361     s/\s+/ /g;
362     s/^\s*//;
363     $email= s/ *\<[^<>()]+\>$//g ? $& : '';
364     $_= "$1 $_" if s/ (\S+)$//;
365     $_.= $email;
366     $_;
367 }
368
369 sub maintencoded {
370     return $maintencoded{$_[0]} if defined($maintencoded{$_[0]});
371     local ($input)= @_;
372     local ($todo,$encoded)= ($input);
373     while ($todo =~ m/\W/) {
374         $encoded.=$`.sprintf("-%02x_",unpack("C",$&));
375         $todo= $';
376     }
377     $encoded.= $todo;
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;
384 }
385
386 for $tmaint (keys %countpermaint) {
387     $_= $tmaint;
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+$//;
394     $sort1d= $_;
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);
401 }
402
403 sub heading ($$) {
404     my ($pt,$sv) = @_;
405     return $displayshowseverities{$sv}.' - '.$displayshowpendings{$pt};
406 }
407
408 sub makeindex ($$$) {
409     my ($varprefix,$varsuffix,$tkey) = @_;
410     my ($pending,$severity,$anydone,$text);
411     $anydone= 0;
412     $text= '';
413     for $pending (qw(pending forwarded done)) {
414         for $severity (@showseverities) {
415             $estr= "\$value= \\${varprefix}${pending}${severity}${varsuffix}; 1;";
416 #print STDERR $estr;
417             eval $estr
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".
424                     $$value.
425                     "</ul>\n";
426             $anydone=1 if $pending eq 'done';
427         }
428     }
429     $text.= $expirynote_html if $anydone;
430     return $text;
431 }        
432
433 &file("ix/full.html",'def',
434       $gFullIndex.
435       makeindex('$index',"",'').
436       "<hr>\n".
437       $tail_html."</body><html>\n");
438
439 &file("ju/junk.html",'non',
440       $gJunkIndex.
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".
443       $indexunmatched.
444       "</ul><hr>\n".
445       $tail_html."</body><html>\n");
446
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>.
453
454 ";
455
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".
466               $otherindex_html.
467               ($pending eq 'done' ? "<P>\n$expirynote_html" : '').
468               "<hr>\n<ul>\n".
469               $$value.
470               "</ul>\n<hr>\n".
471               $tail_html."</body></html>\n");
472     }
473 }
474
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);
480     $itext='';
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".
492                   "  ($xitext)\n";
493         $backnext= '';
494         if ($i>0) {
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>".
500                          "$xitext\n";
501         }
502         if ($i<$#$keysref) {
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>".
508                          "$xitext\n";
509         }
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).
515               $caveat.
516               "See the <A href=\"../$filename\">listing of $whatplural</A>.\n".
517               $backnext.
518               &makeindex("\$per${abbrev}","{\$tkey}",$tkey).
519               "<hr>\n".
520               $tail_html."</body></html>\n");
521     }
522     &file($filename,'non',
523           $ihead.
524           "<hr><ul>\n".
525           $itext.
526           "</ul><hr>\n".
527           $tail_html."</body></html>\n");
528 }
529
530 @maintainers= sort { $maintsort{$a} cmp $maintsort{$b}; } keys %countpermaint;
531 individualindexes('ix/maintainers.html',
532                   @maintainers,
533                   sub { 'ma/l'.&maintencoded($_[0]).'.html'; },
534                   %countpermaint,
535                   sub { $maintdisplay{$_[0]}; },
536                   sub { &sani($_[0]); },
537                   'maintainer',
538                   "Note that there may be other reports filed under different
539                                   variations on the maintainer\'s name and email address.<P>",
540                   'maintainers',
541                   'maint',
542                   $gMaintIndex,
543                   sub { return ''; },
544                   sub { return ''; });
545
546 @packages= sort keys %countperpack;
547 individualindexes('ix/packages.html',
548                   @packages,
549                   sub { length($_[0]) ? "pa/l$_[0].html" : 'pa/none.html'; },
550                   %countperpack,
551                   sub { length($_[0]) ? $_[0] : 'not specified'; },
552                   sub { &sani(length($_[0]) ? $_[0] : 'not specified'); },
553                   'package',
554                   "Note that with multi-binary packages there may be other
555                                   reports filed under the different binary package names.<P>",
556                   'packages',
557                   'pack',
558                   $gPackageIndex,
559                   sub {
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";
565                   },
566                   sub {
567                       return unless defined($maintainer{$_[0]});
568                       $tmaint= $maintainer{$_[0]};
569                       return "<A href=\"../ma/l".
570                              &maintencoded($tmaint).
571                              ".html\">".&sani($tmaint)."</A>";
572                   });
573
574 &file('ix/summary.html','non',
575       $gSummaryIndex.
576       "<hr><pre>\n".
577       $shortindex.
578       "</pre><hr>\n".
579       $tail_html."</body></html>\n");
580
581 $bypackageindex='';
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',
587       $gPackageLog.
588       "<hr><pre>\n$shorthead\n".
589       $bypackageindex.
590       "</pre><hr>\n".
591       $tail_html."</body></html>\n");
592
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',
597       $gPseudoIndex.
598       "<hr><pre>\n$ppd".
599       "</pre><hr>\n".
600       $tail_html."</body></html>\n");
601
602 $_= $gHTMLStamp; s/SUBSTITUTE_DTIME/$dtime/o;
603
604 &file('ix/zstamp.html','non',$_."</body></html>\n");
605
606 sub notimestamp ($) {
607     $_= $_[0];
608     s/\<\!\-\-timestamp\-\-\>\n.*\n\<\!\-\-\/timestamp\-\-\>\n//;
609     return $_;
610 }
611
612 sub file {
613     local ($name,$ii,$file)= @_;
614     if ($diff) {
615         $cmppath= "$wwwbase/$name".($ii eq 'ref' ? '.ref' : '');
616         if (open(ORIG,"$cmppath")) {
617             undef $/; $orig= <ORIG>; $/= "\n";
618             close(ORIG);
619             if (&notimestamp($orig) eq &notimestamp($file)) {
620                 print "preserve $name\n";
621                 return;
622             }
623             defined($c= open(P,"-|")) or die "pipe/fork for diff: $!";
624             if (!$c) {
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";
628                 exit($?>>8);
629             }
630             undef $/; $difftxt= <P>; $/= "\n";
631             close(P); $?==0 || $?==256 or die "diff fork gave $?\n";
632             if ($?==0) {
633                 print "preserve $name\n";
634                 return;
635             }
636             $v= (split(/\n/,$difftxt));
637             print "diff $v $ii $name\n${difftxt}thatdiff $name\n"
638                 or die "stdout (diff): $!";
639             return;
640         }
641     } 
642     $v= (split(/\n/,$file));
643     print "file $v $ii $name\n${file}thatfile $name\n" or die "stdout: $!";
644 }
645
646 sub preserve {
647     print "preserve $_[0]\n";
648 }
649
650 print "end\n";
651
652 while ($u= $cleanups[$#cleanups]) { &$u; }
653 exit 0;