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