1#!perl -w
2# $Id: make_issues.pl,v 1.3 2004/09/30 16:09:27 jlinoff Exp $
3# ================================================
4# Copyright Notice
5# Copyright (C) 1998-2004 by Joe Linoff (http://www.joelinoff.com)
6#
7# Permission is hereby granted, free of charge, to any person obtaining
8# a copy of this software and associated documentation files (the
9# "Software"), to deal in the Software without restriction, including
10# without limitation the rights to use, copy, modify, merge, publish,
11# distribute, sublicense, and/or sell copies of the Software, and to
12# permit persons to whom the Software is furnished to do so, subject to
13# the following conditions:
14#
15# The above copyright notice and this permission notice shall be
16# included in all copies or substantial portions of the Software.
17#
18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21# IN NO EVENT SHALL JOE LINOFF BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24# OTHER DEALINGS IN THE SOFTWARE.
25#
26# Comments and suggestions are always welcome.
27# ================================================
28#
29# Generate an HTML FAQ by parsing a text file
30# with some embedded keywords.
31#
32# This script recognizes certain keywords.
33#
34# All keywords must start at the beginning of a line.
35#
36# Comments are delimited by # at the beginning of
37# a line unless you are in a description subsection.
38#
39# The document contains two parts, the page section
40# and the entry sections. The page section describes
41# details about the document itself. The page keywords
42# have the PAGE_ prefix.
43#
44# There is an entry section for each entry in the
45# document. The entry keywords have the ENTRY_ prefix.
46#
47# Here is what an example document might look like
48# with two entries.
49#
50#   # ========================
51#   # This is an example list
52#   # of issues.
53#   # ========================
54#   PAGE_TITLE: Example List
55#   PAGE_AUTHOR: I.M. Author
56#   PAGE_EMAIL: imauthor@mail.net
57#   PAGE_REVISION: $Revision: 1.3 $
58#   PAGE_DATE: $Date: 2004/09/30 16:09:27 $
59#   PAGE_DESC_BEGIN:
60#   This page keeps track of issues.
61#   If is used for a variety of purposes.
62#   PAGE_DESC_END:
63#
64#   # ========================
65#   # Entry 0001
66#   # ========================
67#   ENTRY_ID: 0001
68#   ENTRY_TITLE: There is a bug in the foo stuff.
69#   ENTRY_REPORTED_BY: I.M. Author
70#   ENTRY_REPORTED_ON: 2002/07/29
71#   ENTRY_STATUS: FIXED
72#   ENTRY_RESOLVED_BY: I.M Author
73#   ENTRY_RESOLVED_ON: 2002/07/29
74#   ENTRY_REPORTED_BEGIN:
75#   There is a bug.
76#   ENTRY_REPORTED_END:
77#   ENTRY_RESOLVED_BEGIN:
78#   It was fixed in release 10.
79#   ENTRY_RESOLVED_END:
80#
81#   # ========================
82#   # Entry 0002
83#   # ========================
84#   ENTRY_ID: 0002
85#   ENTRY_TITLE: There is another bug in the foo stuff.
86#   ENTRY_REPORTED_BY: I.M. Author
87#   ENTRY_REPORTED_ON: 2002/07/29
88#   ENTRY_STATUS: OPEN
89#   ENTRY_RESOLVED_BY:
90#   ENTRY_RESOLVED_ON:
91#   ENTRY_REPORTED_BEGIN:
92#   There is another bug.
93#   ENTRY_REPORTED_END:
94#   ENTRY_RESOLVED_BEGIN:
95#   ENTRY_RESOLVED_END:
96#
97# The keywords are described in more detail in the following
98# sections.
99#
100# ================================================
101# PAGE_TITLE: <title>
102#
103#    The title of the page. The keyword and data must exist on one
104#    line.
105#
106#    Here is a usage example:
107#       PAGE_TITLE: <a href="http://foobar.com">The Title</a>
108#
109#    Note that the FAQER will strip out the HTML tags for the page
110#    <title></title> entry so that wierd stuff will not show up at the
111#    top of the browser window.
112#
113# ================================================
114# PAGE_AUTHOR: <name>
115#
116#    The author of the page. The keyword and data must exist on one
117#    line.
118#
119#    Here is a sample usage:
120#       PAGE_AUTHOR: Joe Linoff
121#
122# ================================================
123# PAGE_EMAIL: <email address>
124#
125#    The e-mail address of the author. The keyword and data must
126#    exist on one line.
127#
128#    Here is a sample usage:
129#       PAGE_EMAIL: jdl@xilinx.com
130#
131# ================================================
132# PAGE_REVISION: <date>
133#
134#    The page revision.  The keyword and data must exist on one
135#    line.
136#
137#    Here is a sample usage:
138#       PAGE_REVISION: $Revision: 1.3 $
139#
140# ================================================
141# PAGE_DATE: <date>
142#
143#    The Date this page was generated. The keyword and data must
144#    exist on one line.
145#
146#    Here is a sample usage:
147#       PAGE_DATE: $Date: 2004/09/30 16:09:27 $
148#
149# ================================================
150# PAGE_DESC_BEGIN:
151# <lines>
152# PAGE_DESC_END:
153#
154#    A description of the title page.
155#
156#    The desccription can contain any text, including HTML.
157#    Remember that if you want <,> or & characters
158#    you must use &lt;, &gt; or &amp;
159#
160#    Here is an example usage:
161#       PAGE_DESC_BEGIN:
162#       This is a the page of issues.
163#       PAGE_DESC_END:
164#
165# ================================================
166# ENTRY_ID: <id>
167#
168#    This describes a unique entry id. I use numbers but this can be
169#    anything. The script verifies that each entry id is unique. The
170#    keyword and data must exist on one line.
171#
172#    Here is an example usage:
173#       ENTRY_ID: 0001
174#
175# ================================================
176# ENTRY_TITLE: <title>
177#
178#    This describes the entry title. The keyword and data must exist
179#    on one line.
180#
181#    Here is an example usage:
182#       ENTRY_TITLE: Core dump in foo when -aa 100 is specified.
183#
184# ================================================
185# ENTRY_REPORTED_BY: <name>
186#
187#    This describes the author of the entry. This is usually
188#    the person who reported it. I sometimes embed the
189#    the email information. The keyword and data must exist
190#    on one line.
191#
192#    Here is an example usage:
193#       ENTRY_REPORTED_BY: <a href="mailto:jdl@xilinx.com">Joe</a>
194#
195# ================================================
196# ENTRY_REPORTED_ON: <date>
197#
198#    This describes the date that the entry was reported.  The keyword
199#    and data must exist on one line.
200#
201#    Here is an example usage:
202#       ENTRY_REPORTED_BY: <a href="mailto:jdl@xilinx.com">Joe</a>
203#
204# ================================================
205# ENTRY_STATUS: <string>
206#
207#    The entry status. The user can specify any string but it is best
208#    if it has no white space because it is used to generate
209#    additional HTML files. I typically use statuses like: OPEN,
210#    FIXED, CLOSED, PENDING.  When the script runs it groups entries
211#    based on the status string (the entries are case sensitive) and
212#    generates HTML files with names like issues_OPEN.html and
213#    issues_FIXED.html. I use upper case to avoid problems. The
214#    keyword and data must exist on one line.
215#
216#    Here is an example usage:
217#       ENTRY_STATUS: OPEN
218#
219# ================================================
220# ENTRY_RESOLVED_BY: <name>
221#
222#    This describes the author of the entry. This is usually
223#    the person who resolved it. I sometimes embed the
224#    the email information. The keyword and data must exist
225#    on one line.
226#
227#    Here is an example usage:
228#       ENTRY_RESOLVED_BY: <a href="mailto:jdl@xilinx.com">Joe</a>
229#
230# ================================================
231# ENTRY_RESOLVED_ON: <date>
232#
233#    This describes the date that the entry was resolved.  The keyword
234#    and data must exist on one line.
235#
236#    Here is an example usage:
237#       ENTRY_RESOLVED_BY: <a href="mailto:jdl@xilinx.com">Joe</a>
238#
239# ================================================
240# ENTRY_REPORTED_BEGIN:
241# <lines>
242# ENTRY_REPORTED_END:
243#
244#    A description of the entry.
245#
246#    The desccription can contain any text, including HTML.
247#    Remember that if you want <,> or & characters
248#    you must use &lt;, &gt; or &amp;
249#
250#    Here is an example usage:
251#       ENTRY_REPORTED_BEGIN:
252#       There is a problem.
253#       ENTRY_REPORTED_END:
254#
255# ================================================
256# ENTRY_RESOLVED_BEGIN:
257# <lines>
258# ENTRY_RESOLVED_END:
259#
260#    A description of the resolution.
261#
262#    The desccription can contain any text, including HTML.
263#    Remember that if you want <,> or & characters
264#    you must use &lt;, &gt; or &amp;
265#
266#    Here is an example usage:
267#       ENTRY_RESOLVED_BEGIN:
268#       This entry was resolved by fixing the problem.
269#       ENTRY_RESOLVED_END:
270#
271#
272use strict;
273
274&Main;
275
276# ================================================================
277# MAIN
278# ================================================================
279sub Main
280{
281    my $ifile = "";
282    my $ofile = "";
283    my $verbose = 0;
284    while ( $#ARGV>=0 ) {
285	my $arg = shift @ARGV;
286	if ( $arg eq "-i" ) {
287	    $ifile = shift @ARGV;
288	    next;
289	}
290	if ( $arg eq "-o" ) {
291	    $ofile = shift @ARGV;
292	    next;
293	}
294	if ( $arg eq "-h" ) {
295	    &Usage;
296	}
297	if ( $arg eq "-v" ) {
298	    $verbose = 1;
299	    next;
300	}
301	print STDERR "ERROR: Unrecognized switch '$arg'.\n";
302	&Usage;
303    }
304    if ( $ifile eq "" ) {
305	print STDERR "ERROR: -i <file> not specified.\n";
306	&Usage;
307    }
308    if ( $ofile eq "" ) {
309	print STDERR "ERROR: -o <file> not specified.\n";
310	&Usage;
311    }
312    &Process($ifile,$ofile);
313}
314# ================================================================
315# Process the data and create the HTML output.
316# ================================================================
317sub Process
318{
319    my $ifile = shift;
320    my $ofile = shift;
321
322    # ================================================
323    # Set the default page records.
324    # ================================================
325    my %page = ();
326    $page{"title"} = "Unknown";
327    $page{"author"} = "Unknown";
328    $page{"email"} = "";
329    $page{"revision"} = "Unknown";
330    $page{"date"} = "Unknown";
331    $page{"description"} = "Unknown";
332
333    # ================================================
334    # Process the input file.
335    # ================================================
336    my %entry = ();
337    my %status = ();
338    my $num_entries = 0;
339    my $entryid = "";
340    my $err = 0;
341    my $lineno = 1;
342    open(IFILE,"$ifile") || die "ERROR: Can't read '$ifile'.\n";
343    while(<IFILE>) {
344	$lineno++;
345	chop;
346	my $line = $_;
347	if ( $line =~ /^PAGE_TITLE:\s+(.*)$/ ) {
348	    $page{"title"} = $1;
349	    next;
350	}
351	if ( $line =~ /^PAGE_AUTHOR:\s+(.*)$/ ) {
352	    $page{"author"} = $1;
353	    next;
354	}
355	if ( $line =~ /^PAGE_EMAIL:\s+(.*)$/ ) {
356	    $page{"email"} = $1;
357	    next;
358	}
359	if ( $line =~ /^PAGE_REVISION:\s+(.*)$/ ) {
360	    $page{"revision"} = $1;
361	    next;
362	}
363	if ( $line =~ /^PAGE_DATE:\s+(.*)$/ ) {
364	    $page{"date"} = $1;
365	    next;
366	}
367	if ( $line =~ /^PAGE_DESC_BEGIN:\s*$/ ) {
368	    my $desc = "";
369	    while(<IFILE>) {
370		chop;
371		$line = $_;
372		last if ( $line =~ /^PAGE_DESC_END:\s*$/ );
373		$desc = "$desc\n$line";
374	    }
375	    $page{"description"} = $desc;
376	    next;
377	}
378	if ( $line =~ /^ENTRY_ID:\s+(.*)$/ ) {
379	    $num_entries++;
380	    $entryid = $1;
381	    if ( defined $entry{$entryid} ) {
382		print STDERR "ERROR: Duplicate entry id '$entryid' found at line $lineno.\n";
383		$err++;
384	    }
385	    $entry{$entryid}{"title"} = "";
386	    $entry{$entryid}{"reported_by"} = "";
387	    $entry{$entryid}{"reported_on"} = "";
388	    $entry{$entryid}{"reported_description"} = "";
389	    $entry{$entryid}{"status"} = "";
390	    $entry{$entryid}{"resolved_by"} = "";
391	    $entry{$entryid}{"resolved_on"} = "";
392	    $entry{$entryid}{"resolved_description"} = "";
393	    next;
394	}
395	if ( $line =~ /^ENTRY_TITLE:\s+(.*)$/ ) {
396	    $entry{$entryid}{"title"} = $1;
397	    next;
398	}
399	if ( $line =~ /^ENTRY_REPORTED_BY:\s+(.*)$/ ) {
400	    $entry{$entryid}{"reported_by"} = $1;
401	    next;
402	}
403	if ( $line =~ /^ENTRY_REPORTED_ON:\s+(.*)$/ ) {
404	    $entry{$entryid}{"reported_on"} = $1;
405	    next;
406	}
407	if ( $line =~ /^ENTRY_STATUS:\s*(.*)$/ ) {
408	    $entry{$entryid}{"status"} = $1;
409	    if ( $1 ne "" ) {
410		if ( ! defined( $status{$1} ) ) {
411		    $status{$1} = 0;
412		}
413		my $x = $status{$1};
414		my $y = $x + 1;
415		$status{$1} = $y;
416	    }
417	    next;
418	}
419	if ( $line =~ /^ENTRY_RESOLVED_BY:\s*(.*)$/ ) {
420	    $entry{$entryid}{"resolved_by"} = $1;
421	    next;
422	}
423	if ( $line =~ /^ENTRY_RESOLVED_ON:\s*(.*)$/ ) {
424	    $entry{$entryid}{"resolved_on"} = $1;
425	    next;
426	}
427	if ( $line =~ /^ENTRY_REPORTED_BEGIN:\s*$/ ) {
428	    my $desc = "";
429	    while(<IFILE>) {
430		chop;
431		$line = $_;
432		last if ( $line =~ /^ENTRY_REPORTED_END:\s*$/ );
433		if ( $line =~ /^ENTRY_LINK:\s*(\S*)\s+(.*)$/ ) {
434		    # ENTRY_LINK: <id> <desc>
435		    $desc = "$desc\n<a href=#TAG_$1>$2</a>";
436		}
437		else {
438		    $desc = "$desc\n$line";
439		}
440	    }
441	    $entry{$entryid}{"reported_description"} = $desc;
442	    next;
443	}
444	if ( $line =~ /^ENTRY_RESOLVED_BEGIN:\s*$/ ) {
445	    my $desc = "";
446	    while(<IFILE>) {
447		chop;
448		$line = $_;
449		last if ( $line =~ /^ENTRY_RESOLVED_END:\s*$/ );
450		if ( $line =~ /^ENTRY_LINK:\s*(\S*)\s+(.*)$/ ) {
451		    # ENTRY_LINK: <id> <desc>
452		    $desc = "$desc\n<a href=#TAG_$1>$2</a>";
453		}
454		else {
455		    $desc = "$desc\n$line";
456		}
457	    }
458	    $entry{$entryid}{"resolved_description"} = $desc;
459	    next;
460	}
461	if ( $line =~ /^(ENTRY\S+:)/ ) {
462	    print STDERR "ERROR: Unrecognized token '$1' at line $lineno\n";
463	    $err++;
464	    next;
465	}
466	if ( $line =~ /^(PAGE\S+:)/ ) {
467	    print STDERR "ERROR: Unrecognized token '$1' at line $lineno\n";
468	    $err++;
469	    next;
470	}
471    }
472    close(IFILE);
473
474    exit $err if ( $err );
475
476    # ================================================
477    # Write the master page.
478    # ================================================
479    my @tokens = split(/\./,$ofile);
480    my $rootfn = $tokens[0];
481    my $rootext = $tokens[1];
482    my $sumfile = "${rootfn}-summary.html";
483    print "Creating $ofile ...\n";
484    open(OFILE,">$ofile") || die "ERROR: Can't write '$ofile'.\n";
485    # Create the header
486    &PrintHtmlHeader(\*OFILE,
487		     "$page{title}",
488		     "All",
489		     "$page{author}",
490		     "$page{date}",
491		     "$page{revision}",
492		     "Summary",
493		     "$sumfile");
494    print OFILE "$page{description}\n";
495    # Create the summary table.
496    &PrintHtmlStatusTable(\*OFILE,
497			  \%status,
498			  $num_entries,
499			  $ofile,
500			  "$rootfn",
501			  "$rootext");
502    # entry summary table
503    &PrintHtmlEntrySummaryTable(\*OFILE,
504				\%entry,
505				"all",
506				"");
507    # Entries
508    &PrintHtmlEntries(\*OFILE,
509		      \%entry,
510		      "all");
511    # End of the file.
512    &PrintHtmlTrailer(\*OFILE,
513		      "$page{email}",
514		      "$page{author}",
515		      "$page{date}");
516    # write the master output file.
517    close OFILE;
518
519    # ================================================
520    # Write the summary page.
521    # ================================================
522    print "Creating $sumfile ...\n";
523    open(OFILE,">$sumfile") || die "ERROR: Can't write '$sumfile'.\n";
524    # Create the header
525    &PrintHtmlHeader(\*OFILE,
526		     "$page{title}",
527		     "Summary",
528		     "$page{author}",
529		     "$page{date}",
530		     "$page{revision}",
531		     "All",
532		     "$ofile");
533    # Create the summary table.
534    &PrintHtmlStatusTable(\*OFILE,
535			  \%status,
536			  $num_entries,
537			  $sumfile,
538			  "$rootfn",
539			  "$rootext");
540    # entry summary table
541    &PrintHtmlEntrySummaryTable(\*OFILE,
542				\%entry,
543				"all",
544				"$tokens[0].html");
545    # End of the file.
546    &PrintHtmlTrailer(\*OFILE,
547		      "$page{email}",
548		      "$page{author}",
549		      "$page{date}");
550    # write the master output file.
551    close OFILE;
552
553    # ================================================
554    # Write pages for each status.
555    # ================================================
556    my $key;
557    foreach $key ( sort keys %status ) {
558	my $fn = "${rootfn}_$key.${rootext}";
559	print "Creating $fn ...\n";
560	open(OFILE,">$fn") || die "ERROR: Can't write '$fn'.\n";
561	# Create the header
562	&PrintHtmlHeader(\*OFILE,
563			 "$page{title}",
564			 "$key Status",
565			 "$page{author}",
566			 "$page{date}",
567			 "$page{revision}",
568			 "All",
569			 $ofile,
570			 "$rootfn",
571			 "$rootext");
572	# entry summary table
573	&PrintHtmlEntrySummaryTable(\*OFILE,
574				    \%entry,
575				    $key,
576				    "");
577	# Entries
578	&PrintHtmlEntries(\*OFILE,
579			  \%entry,
580			  $key);
581	# End of the file.
582	&PrintHtmlTrailer(\*OFILE,
583			  "$page{email}",
584			  "$page{author}",
585			  "$page{date}");
586	# write the master output file.
587	close OFILE;
588    }
589}
590# ================================================================
591# Print the header
592# ================================================================
593sub PrintHtmlHeader
594{
595    my $out = shift;
596    my $title = shift;
597    my $subtitle = shift;
598    my $author = shift;
599    my $date = shift;
600    my $revision = shift;
601    my $link = shift;
602    my $href = shift;
603
604    print $out "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n";
605    print $out "<!-- Subroutine: PrintHtmlHeader -->\n";
606    print $out "<html>\n";
607    print $out "<head>\n";
608
609    if( $title =~ /\<\/a>/ ) {
610	# Strip out the formatting tags for the title.
611	my $tmp = $title;
612	while ( $tmp =~ /<[^>]+>/ ) {
613	    $tmp =~ s/<[^>]+>/ /g;
614	}
615	print $out "<title>${tmp}</title>\n";
616    }
617    else {
618	print $out "<title>${title}</title>\n";
619    }
620
621    print $out "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n";
622    print $out "</head>\n";
623    print $out "<body bgcolor=white>\n";
624    print $out "<a name=top></a>\n";
625    print $out "<center>\n";
626
627    print $out "<font size=\"+2\"><b>${title}</b></font>";
628    print $out "<br><font size=\"+2\"><b>${subtitle}</b></font>";
629    if( $link ne "" && $href ne "" ) {
630	print $out "<br><b><a href=\"${href}\">${link}</a></b>\n";
631    }
632
633    print $out "<br>\n";
634    print $out "${author}\n";
635    print $out "<br>\n";
636    print $out "${date}\n";
637    print $out "<br>\n";
638    print $out "<font size=\"-1\">${revision}</font>\n";
639    print $out "</center>\n";
640}
641# ================================================================
642# Print the header
643# ================================================================
644sub PrintHtmlTrailer
645{
646    my $out = shift;
647    my $email = shift;
648    my $author = shift;
649    my $date = shift;
650
651    print $out "<!-- Subroutine: PrintHtmlTrailer -->\n";
652    print $out "<hr noshade width=\"100%\" size=2>\n";
653    print $out "<center>\n";
654    print $out "<font size=\"-1\">\n";
655    print $out "<a href=\"#top\">[Top]</a>\n";
656    print $out "<br>\n";
657    print $out "This page is maintained by <a href=\"mailto:${email}\">${author}</a>.\n";
658    print $out "<br>\n";
659    print $out "Last updated: ${date}\n";
660    print $out "<br>\n";
661    print $out "<br>\n";
662    print $out "<i>This page was automatically generated by issues.pl.</i>\n";
663    print $out "</font>\n";
664    print $out "</center>\n";
665    print $out "</body>\n";
666    print $out "</html>\n";
667}
668# ================================================================
669# Status summary table.
670# ================================================================
671sub PrintHtmlStatusTable
672{
673    my $out = shift;
674    my $ht = shift;
675    my $num_entries = shift;
676    my $ofile = shift;
677    my $rootfn = shift;
678    my $rootext = shift;
679
680    my %status = %$ht;
681    my $key;
682
683    print $out "<!-- Subroutine: PrintHtmlStatusTable -->\n";
684    print $out "<hr noshade width=\"100%\" size=2>\n";
685    print $out "<center>\n";
686    print $out "<font size=\"+1\">Status Summary</font><br>\n";
687    print $out "<table border=1>\n";
688    print $out "<tr>\n";
689    print $out "<th>Status\n";
690    print $out "<th>Number\n";
691    foreach $key ( sort keys %status ) {
692	my $fn = "${rootfn}_$key.${rootext}";
693	print $out "</tr>\n";
694	print $out "<tr>\n";
695	print $out "<td><a href=\"$fn\">$key</a></td>\n";
696	print $out "<td align=right>$status{$key}</td>\n";
697    }
698    print $out "</tr>\n";
699    print $out "<tr>\n";
700    print $out "<td><b>Total</b></td>\n";
701    print $out "<td align=right>$num_entries</td>\n";
702    print $out "</tr>\n";
703    print $out "</table>\n";
704    print $out "</center>\n";
705}
706# ================================================================
707# Entry summary table.
708# ================================================================
709sub PrintHtmlEntrySummaryTable
710{
711    my $out = shift;
712    my $ht = shift;
713    my $match = shift;
714    my $href_prefix = shift;
715
716    my %entry = %$ht;
717    my $key;
718
719    print $out "<!-- Subroutine: PrintHtmlEntrySummaryTable -->\n";
720    print $out "<a name=summary></a>\n";
721    print $out "<hr noshade width=\"100%\" size=2>\n";
722    print $out "<center>\n";
723    print $out "<font size=\"+1\">Entry Summary</font><br>\n";
724    print $out "<table border=1>\n";
725    print $out "<tr>\n";
726    print $out "<th>Id\n";
727    print $out "<th>Title\n";
728    print $out "<th>Status\n";
729    foreach $key ( sort keys %entry ) {
730	my $status = $entry{$key}{status};
731	next if( $status ne $match && $match ne "all" );
732	print $out "</tr>\n";
733	print $out "<tr>\n";
734	print $out "<td><a href=\"${href_prefix}#TAG_$key\">$key</a></td>\n";
735	print $out "<td>$entry{$key}{title}</td>\n";
736	print $out "<td>$entry{$key}{status}</td>\n";
737    }
738    print $out "</tr>\n";
739    print $out "</table>\n";
740    print $out "</center>\n";
741}
742# ================================================================
743# Entries
744# ================================================================
745sub PrintHtmlEntries
746{
747    my $out = shift;
748    my $ht = shift;
749    my $match = shift;
750
751    my %entry = %$ht;
752    my $key;
753    my @xkeys = sort keys %entry;
754    my @keys = ();
755    foreach $key ( @xkeys ) {
756	my $status = $entry{$key}{status};
757	next if( $status ne $match && $match ne "all" );
758	push @keys,$key;
759    }
760    my $previous = "";
761    my $next = "";
762    my $i = 0;
763    foreach $key ( @keys ) {
764	$i++;
765	$previous = "";
766	$previous = $keys[$i-2] if($i-2>=0);
767	$next = "";
768	$next = $keys[$i] if($i<=$#keys);
769	&PrintHtmlEntry($out,$ht,$key,$next,$previous);
770    }
771}
772# ================================================================
773# Entries
774# ================================================================
775sub PrintHtmlEntry
776{
777    my $out = shift;
778    my $ht = shift;
779    my $key = shift;
780    my $next = shift;
781    my $previous = shift;
782
783    my %entry = %$ht;
784
785    print $out "<!-- Subroutine: PrintHtmlEntry -->\n";
786    print $out "<a name=TAG_$key></a>\n";
787    print $out "<hr noshade width=\"100%\" size=2>\n";
788
789    print $out "<table width=\"100%\"  border=0 cellspacing=0 cellpadding=0>\n";
790    print $out "<tr valign=top>\n";
791    print $out "<td>\n";
792
793    print $out "<table border=0 cellspacing=0 cellpadding=0>\n";
794    print $out "<tr><td><b>Title: </b></td><td>$entry{$key}{title}</td></tr>\n";
795    print $out "<tr><td><b>Status: </b></td><td>$entry{$key}{status}</td></tr>\n";
796    print $out "<tr><td><b>Reported By: </b></td><td>$entry{$key}{reported_by}</td></tr>\n";
797    print $out "<tr><td><b>Reported On: </b></td><td>$entry{$key}{reported_on}</td></tr>\n";
798    print $out "<tr><td><b>Resolved By: </b></td><td>$entry{$key}{resolved_by}</td></tr>\n";
799    print $out "<tr><td><b>Resolved On: </b></td><td>$entry{$key}{resolved_on}</td></tr>\n";
800    print $out "</table>\n";
801    print $out "</td>\n";
802
803    print $out "<td align=right>\n";
804    print $out "<b>Id: </b>$key\n";
805    print $out "<br>\n";
806    print $out "<font size=\"-1\"><a href=\"#top\">[Top]</a></font>\n";
807    print $out "<br>\n";
808    print $out "<font size=\"-1\"><a href=\"#summary\">[Summary]</a></font>\n";
809    if ( $next ne "" ) {
810	print $out "<br>\n";
811	print $out "<font size=\"-1\"><a href=\"#TAG_$next\">[Next]</a></font>\n";
812    }
813    if ( $previous ne "" ) {
814	print $out "<br>\n";
815	print $out "<font size=\"-1\"><a href=\"#TAG_$previous\">[Previous]</a></font>\n";
816    }
817
818    print $out "</td>\n";
819    print $out "</tr>\n";
820    print $out "</table>";
821
822    print $out "<b>Problem Description: </b>\n";
823    print $out "<blockquote>\n";
824    print $out "$entry{$key}{reported_description}\n";
825    print $out "</blockquote>\n";
826
827    print $out "<b>Solution Description: </b>\n";
828    print $out "<blockquote>\n";
829    print $out "$entry{$key}{resolved_description}\n";
830    print $out "</blockquote>\n";
831}
832# ================================================================
833# Report the usage and exit.
834# ================================================================
835sub Usage
836{
837    my $rev = '$Id: make_issues.pl,v 1.3 2004/09/30 16:09:27 jlinoff Exp $'; #'
838    print STDERR "\n";
839    print STDERR "$rev\n";
840    print STDERR "\n";
841    print STDERR "usage: perl issues.pl [args]\n";
842    print STDERR "\n";
843    print STDERR "  -h         On-line help.\n";
844    print STDERR "  -i <file>  The input file\n";
845    print STDERR "  -o <file>  The output HTML file\n";
846    print STDERR "  -v         Verbose mode.\n";
847    print STDERR "\n";
848
849    exit 0;
850}
851