#!/usr/bin/perl -w

use strict;
use XML::Parser;

if ($#ARGV > 1 || $#ARGV < 0)
{
   die "Syntax: $0 <lang> [<region>]\n";
}

my $targetdir = '../resources/helpsets';
my $dictdir = '../resources/dictionaries';
my $mainclass = '../java/DatatoolTk.java';
my $base = 'datatooltk';

my $fontenc = 'T1';

# mapping between locales and babel options

my %babel =
 (
    'en'    => 'english',
    'en-GB' => 'british',
    'en-US' => 'american',
    'fr'    => 'frenchb'
 );

my $lang = $ARGV[0];

my $locale = $lang;

if ($#ARGV == 1)
{
  $locale .= "-".$ARGV[1];
}

my $encoding_file = 'utf8';
my $encoding_xml  = 'UTF-8';

open FD, "$mainclass" or die "Can't open '$mainclass' $!\n";
binmode(FD, ":$encoding_file");

my %dict = ();

while (<FD>)
{
   if (/public +static +final +String +appName *= *"([^"]+)"/)
   {
      $dict{appname} = $1;
   }
   elsif (/public +static +final +String +appVersion *= *"([^"]+)"/)
   {
      $dict{appversion} = $1;
   }
   elsif (/public +static +final +String +appDate *= *"([^"]+)"/)
   {
      $dict{appdate} = $1;
   }
}

close FD;

my $propfile = "$dictdir/$base-$locale.prop";

if (not (-e $propfile) and ($lang ne $locale))
{
   $propfile = "$dictdir/$base-$lang.prop";

   unless (-e $propfile)
   {
      die "Can't find either '$dictdir/$base-$locale.prop' or
'$propfile'\n";
   }
}

unless (-e $propfile)
{
   die "Can't find '$propfile'\n";
}

open FD, "$propfile" or die "Can't open '$propfile' $!\n";
binmode(FD, ":$encoding_file");

my $linenum = 0;

while (<FD>)
{
   $linenum++;

   next if (/^ *#/ or /^ *$/);

   if (/^([^=]+)=(.*)$/)
   {
      $dict{$1} = $2;
   }
   else
   {
      warn "$propfile: Can't parse line $linenum\n";
   }
}

close FD;

my %docinfo = ();
my @nodes   = ();
my @current = ();
my @paragraph = ();
my %tags    = ();

my $languagedefinitions = 
  "\\def\\idxpackagename{".&get_label('manual.idxpackage')."}\n"
 ."\\def\\idxenvname{".&get_label('manual.idxenv')."}\n"
 ."\\def\\examplename{".&get_label('manual.example')."}\n";

my $xmlfile = "$base-$locale.xml";

if (not (-e $xmlfile) and ($lang ne $locale))
{
   $xmlfile = "$base-$lang.xml";

   unless (-e $xmlfile)
   {
      die "Can't find either '$base-$locale.xml' or '$xmlfile'\n";
   }
}

unless (-e $xmlfile)
{
   die "Can't find '$xmlfile'\n";
}

my $texfile = "$base-$locale.tex";
my $helpsetdir = "$targetdir/$base-$locale";

unless (-e $helpsetdir)
{
   mkdir $helpsetdir or die "Can't mkdir '$helpsetdir' $!\n";
}

unless (-d $helpsetdir)
{
   die "'$helpsetdir' is not a directory\n";
}

# Is there any way of querying the input encoding picked up by
# XML::Parser? Let's just stick to utf8
my $inputenc = 'utf8';
 
my %section_levels =
(
   'chapter'       => '1',
   'section'       => '2',
   'subsection'    => '3',
   'subsubsection' => '4',
   'paragraph'     => '5'
);

my %acros = ();

my %terms = ();

my @float_elements = ();

my @tabular_specs = ();

my %counters = ();

# open the TeX file
open TEXFD, ">$texfile" or die "Can't open '$texfile' $!\n";
 
# initialize the parser
my $parser = XML::Parser->new( Handlers => 
                                     {
                                      Start=>\&handle_start,
                                      End=>\&handle_end,
                                      Char=>\&handle_char
                                     });
$parser->parsefile( $xmlfile );
 
close TEXFD;

# Write helpset files

open HSFD, ">$helpsetdir/$base.hs"
  or die "Can't open '$helpsetdir/$base.hs' $!\n";
binmode(HSFD, ":$encoding_file");

my $toctitle = &get_label('manual.toc');
my $manualtitle = &get_label('manual.title');
my $indextitle = &get_label('manual.index');
my $searchtitle = &get_label('manual.search');

print HSFD <<_END_HS_HEAD;
<?xml version='1.0' encoding='$encoding_xml' ?>
<!DOCTYPE helpset
PUBLIC "-//Sun Microsystems Inc.//DTD JavaHelp HelpSet Version 2.0//EN"
"http://java.sun.com/products/javahelp/helpset_2_0.dtd">


<helpset version="2.0">
   <!-- title -->
   <title>$manualtitle</title>
   <!-- maps -->
   <maps>
   <homeID>top </homeID>
   <mapref location="Map.jhm" />
   </maps>

   <!-- views -->
   <view xml:lang="$locale" mergetype="javax.help.UniteAppendMerge">
      <name>TOC</name>
      <label>$toctitle</label>
      <type>javax.help.TOCView</type>
      <data>${base}TOC.xml</data>
   </view>
   <view xml:lang="$locale" mergetype="javax.help.SortMerge">
      <name>Index</name>
      <label>$indextitle</label>
      <type>javax.help.IndexView</type>
      <data>${base}Index.xml</data>
   </view>
   <view xml:lang="$locale">
      <name>Search</name>
      <label>$searchtitle</label>
      <type>javax.help.SearchView</type>
         <data engine="com.sun.java.help.search.DefaultSearchEngine">
         JavaHelpSearch
         </data>
   </view>
   <!-- presentation windows -->
   <!-- main window -->
   <presentation default=true>
      <name>main window</name>
      <size width="600" height="600" />
      <location x="200" y="200" />
      <title>$manualtitle</title>
      <toolbar>
         <helpaction image="homeIcon">javax.help.HomeAction</helpaction>
         <helpaction image="backIcon">javax.help.BackAction</helpaction>
         <helpaction image="forwardIcon">javax.help.ForwardAction</helpaction>
      </toolbar>
      <image>appIcon</image>
   </presentation>
   <!-- secondary window -->
   <presentation displayviews=false>
      <name>secondary window</name>
      <size width="450" height="500" />
      <location x="200" y="200" />
      <image>appIcon</image>
   </presentation>
   <!-- implementation section -->
   <impl>
      <helpsetregistry helpbrokerclass="javax.help.DefaultHelpBroker" />
      <viewerregistry viewertype="text/html"
         viewerclass="com.sun.java.help.impl.CustomKit"/>
      <viewerregistry viewertype="text/xml"
          viewerclass="com.sun.java.help.impl.CustomXMLKit" />
   </impl>
</helpset>
_END_HS_HEAD

close HSFD;

open HTMLFD, ">$helpsetdir/$base.html"
  or die "Can't open '$helpsetdir/$base.html' $!\n";
binmode(HTMLFD, ":$encoding_file");

print HTMLFD &html_head($docinfo{Title}, '', $nodes[0]->{Tag});

print HTMLFD "<body>\n";

print HTMLFD 
   "<div class=\"title\" \"><p>",
   $docinfo{Title}, "</p>\n";

print HTMLFD "<p>$docinfo{Author}</p>\n" if ($docinfo{Author});

print HTMLFD "</div><div class=\"date\">";

print HTMLFD "<p>$docinfo{Date}</p>\n" if ($docinfo{Date});

print HTMLFD "</div>";

print HTMLFD &html_nav('', $nodes[0]);

print HTMLFD "</body>\n</html>\n";

close HTMLFD;

my $prev = $base;

open TOCFD, ">$helpsetdir/${base}TOC.xml" 
   or die "Can't open '$helpsetdir/${base}TOC.xml' $!\n";
binmode(TOCFD, ":$encoding_file");

print TOCFD <<_END_TOC_HEAD;
<?xml version='1.0' encoding='$encoding_xml' ?>
   <!DOCTYPE toc
     PUBLIC "-//Sun Microsystems Inc.//DTD JavaHelp TOC Version
2.0//EN"
     "http://java.sun.com/products/javahelp/toc_2_0.dtd">

<toc version="2.0" xml:lang="en">
<tocitem text="Manual" target="top" >
_END_TOC_HEAD

my $prevlevel = 0;

for (my $idx = 0; $idx <= $#nodes; $idx++)
{
   my $node = $nodes[$idx];

   for (my $level = scalar(keys %section_levels); $level >= 0; $level--)
   {
      if ($section_levels{$node->{Type}}+$level <= $prevlevel)
      {
         print TOCFD "</tocitem>\n";
      }
   }

   print TOCFD "<tocitem text=\"$node->{Title}\" target=\"$node->{Tag}\">\n";

   my $next = ($idx == $#nodes ? '' : $nodes[$idx+1]);

   my $tag = $node->{Tag};

   open HTMLFD, ">$helpsetdir/$tag.html"
     or die "Can't open '$helpsetdir/$tag.html' $!\n";

   binmode(HTMLFD, ":$encoding_file");

   print HTMLFD &html_head($node->{Title}, 
     ($prev eq $base ? $base : $prev->{Tag}),
     ($next ? $next->{Tag} : ''));

   print HTMLFD "<body>\n";

   my $header = 'h'.$section_levels{$node->{Type}};

   print HTMLFD "<$header>", $node->{Title}, "</$header>\n";

   my $content = $node->{Contents};

   $content=~s/<ref>([^<]+)<\/ref>/&get_ref($1)/eg;

   print HTMLFD "<div>$content</div>";

   if ($node->{Footnotes})
   {
      $content = $node->{Footnotes};

      $content=~s/<ref>([^<]+)<\/ref>/&get_ref($1)/eg;

      print HTMLFD "<hr />$content";
   }

   print HTMLFD &html_nav($prev, $next);

   print HTMLFD "</body>\n</html>\n";

   close HTMLFD;

   $prev = $node;
   $prevlevel = $section_levels{$node->{Type}};
}

for (my $level = scalar(keys %section_levels); $level >= 0; $level--)
{
   if ($level < $prevlevel)
   {
      print TOCFD "</tocitem>\n";
   }
}

print TOCFD "</tocitem>\n</toc>\n";

close TOCFD;

# write map file

open MAPFD, ">$helpsetdir/Map.jhm"
  or die "Can't open '$helpsetdir/Map.jhm' $!\n";
binmode(MAPFD, ":$encoding_file");

print MAPFD <<_END_MAP_HEAD;
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE map
 PUBLIC "-//Sun Microsystems Inc.//DTD JavaHelp Map Version 2.0//EN"
     "http://java.sun.com/products/javahelp/map_2_0.dtd">
<map version="2.0" xml:lang="$locale">
_END_MAP_HEAD

print MAPFD "<mapID target=\"top\" url=\"$base.html\" />\n";
print MAPFD "<mapID target=\"appIcon\" url=\"../../icons/datatooltk-logosmall.png\" />\n";
print MAPFD "<mapID target=\"homeIcon\" url=\"../../icons/home.png\" />\n";
print MAPFD "<mapID target=\"backIcon\" url=\"../../icons/back.png\" />\n";
print MAPFD "<mapID target=\"forwardIcon\" url=\"../../icons/forward.png\" />\n";

&hs_map_id(\%tags);
&hs_map_id(\%terms);

print MAPFD "</map>\n";
close MAPFD;

# Write the helpset index file

open IDXFD, ">$helpsetdir/${base}Index.xml"
 or die "Can't open '$helpsetdir/${base}Index.xml' $!\n";
binmode(IDXFD, ":$encoding_file");

print IDXFD <<_END_IDX_HEAD;
<?xml version='1.0' encoding='$encoding_xml' ?>
   <!DOCTYPE index
     PUBLIC "-//Sun Microsystems Inc.//DTD JavaHelp Index Version 2.0//EN"
     "http://java.sun.com/products/javahelp/index_2_0.dtd">

<index version="2.0">
_END_IDX_HEAD

&print_idx_entries(\%terms);

print IDXFD "</index>\n";
close IDXFD;

sub print_idx_entries{
   my ($hash) = @_;

   foreach my $key (keys %$hash)
   {
      if ($hash->{$key}->{Text})
      {
         my $text = $hash->{$key}->{Text};

         $text=~s/<\/?[^>]*>//g;

         print IDXFD "<indexitem text=\"",
           $text, "\" target=\"",
           $key, "\">\n";

         if ($hash->{$key}->{Children})
         {
            &print_idx_entries($hash->{$key}->{Children});
         }

         print IDXFD "</indexitem>\n";
      }
      else
      {
         warn "Text not set for '$key'\n";
      }
   }
}

sub handle_char{
   my( $expat, $string ) = @_;

   &handle_html($string);

   print TEXFD $string;
}

sub hs_map_id{
   my ($hash) = @_;

   foreach my $tag (keys %$hash)
   {
      if ($hash->{$tag}->{Ref})
      {
         print MAPFD "<mapID target=\"$tag\" url=\"$hash->{$tag}->{Ref}\" />\n";
      }
      else
      {
         warn "No Ref found for '$tag'\n";
      }

      if ($hash->{$tag}->{Children})
      {
         &hs_map_id($hash->{$tag}->{Children});
      }
   }
}

sub hs_index_term{
   my ($tag, $text, $term, $hash) = @_;

   if ($hash->{$tag})
   {
      &handle_html($text);
   }
   else
   {
      $hash->{$tag}->{Text} = $term;
      $hash->{$tag}->{Ref}  = $nodes[$#nodes]->{Tag}.".html#$tag";

      &handle_html("<a name=\"$tag\">$text</a>");
   }

}

sub hs_popup{
   my ($href, $text) = @_;

   my $html = <<_END_POPUP;
  <object classid="java:com.sun.java.help.impl.JHSecondaryViewer">
    <param name="viewerStyle" value="javax.help.Popup" />
    <param name="viewerSize" value="400,200" />
    <param name="content" value="$href" />
    <param name="viewerActivator" value="javax.help.LinkLabel" />
    <param name="text" value="&#x261B;" />
    <param name="textColor" value="blue" />
  </object>
_END_POPUP

   $html.$text
}

sub handle_html{
   my ($string) = @_;

   if ($#current > -1)
   {
      my $element = $current[$#current];

      $docinfo{$element} .= $string;
   }
   elsif ($#nodes > -1)
   {
      $nodes[$#nodes]->{Contents} .= $string;
   }
}
 
sub handle_start {
    my( $expat, $element, %attrs ) = @_;

    my $sub = "start_$element";
 
    if (defined(&$sub))
    {
       {
         no strict 'refs';
         &$sub($expat, %attrs);
       }
    }
    else
    {
       die "Don't know what to do with '<$element>'\n";
    }
}

sub handle_end {
    my( $expat, $element ) = @_;
 
    my $sub = "end_$element";

    if (defined(&$sub))
    {
       {
         no strict 'refs';
         &$sub($expat);
       }
    }
    else
    {
       die "Don't know what to do with '</$element>'\n";
    }
}

sub get_ref{
   my ($tag) = @_;

   unless ($tags{$tag})
   {
      die "Unknown tag '$tag'\n";
   }

   my $ref = $tags{$tag}->{Ref};
   my $text = $tags{$tag}->{Text};

   "<a href=\"$ref\">$text</a>";
}

sub get_label{
   my ($tag, $attrs, $expat) = @_;

   unless ($dict{$tag})
   {
      if ($expat)
      {
         die "No dictionary entry for tag '$tag' on line ",
           $expat->current_line, "\n";
      }
      else
      {
         die "No dictionary entry for tag '$tag'\n";
      }
   }

   my $text = $dict{$tag};

   if ($attrs)
   {
      $text=~s/\$(\d)/$attrs->{"arg$1"}/eg;
   }

   # strip any trailing ellipsis

   $text=~s/\.{3}$//;

   $text
}

sub start_manual{
   my ($expat, %attrs) = @_;

   if ($attrs{fontenc})
   {
      $fontenc = $attrs{'fontenc'};
   }

   print TEXFD "% arara: pdflatex\n";
   print TEXFD "% arara: makeglossaries\n";
   print TEXFD "% arara: pdflatex\n";
   print TEXFD "% arara: makeindex\n";
   print TEXFD "% arara: pdflatex\n";
   print TEXFD "\\documentclass[index=totoc,$babel{$locale}]{datatooltk}\n";
}

sub end_manual{
   my ($expat) = @_;
}

sub start_preamble{
   my ($expat, %attrs) = @_;

   print TEXFD "\\usepackage[$inputenc]{inputenc}\n",
      "\\usepackage[$fontenc]{fontenc}\n",
      "\\usepackage{lmodern}\n",
      "\\usepackage{graphicx}\n",
      "\\usepackage{verbatim}\n",
      "\\usepackage{alltt}\n",
      "\\usepackage{moreverb}\n",
      "\\usepackage{makeidx}\n",
      "\\usepackage{babel}\n",
      "\\usepackage[colorlinks]{hyperref}\n",
      "\\usepackage[acronym,toc]{glossaries}\n",
      "\\makeglossaries\n\\makeindex\n",
      $languagedefinitions;
}
 
sub end_preamble{
   my ($expat) = @_;
}

sub start_title{
   my ($expat, %attrs) = @_;

   push @current, 'Title';

   print TEXFD "\\title{";
}

sub end_title{
   my ($expat) = @_;

   pop @current;

   print TEXFD "}\n";
}

sub start_date{
   my ($expat, %attrs) = @_;

   push @current, 'Date';

   print TEXFD "\\date{";
}

sub end_date{
   my ($expat) = @_;

   pop @current;

   print TEXFD "}\n";
}

sub start_author{
   my ($expat, %attrs) = @_;

   push @current, 'Author';

   unless ($docinfo{Author})
   {
      $docinfo{Author} = " ";
   }

   $docinfo{Author} .= "<table class=\"author\"><tr><td>";

   print TEXFD "\\Author{";
}

sub end_author{
   my ($expat) = @_;

   $docinfo{Author} .= "</td></tr></table>";

   pop @current;

   print TEXFD "}\n";
}

sub start_document{
   my ($expat, %attrs) = @_;

   print TEXFD "\\begin{document}\n\\maketitle\n\\tableofcontents\n";
}

sub end_document{
   my ($expat) = @_;

   print TEXFD "\\end{document}\n";
}

sub start_node{
   my ($expat, %attrs) = @_;

   unless ($attrs{type})
   {
      die "Missing 'type' attribute for element 'node' on line ",
         $expat->current_line, "\n";
   }

   unless ($attrs{tag})
   {
      die "Missing 'tag' attribute for element 'node' on line ",
         $expat->current_line, "\n";
   }

   unless ($attrs{title})
   {
      die "Missing 'title' attribute for element 'node' on line ",
         $expat->current_line, "\n";
   }

   my $currentlevel = $section_levels{$attrs{type}};

   unless ($currentlevel)
   {
      die "Unknown section level '$attrs{type}' on line ",
         $expat->current_line, "\n";
   }

   my $lastlevel;

   if ($#nodes == -1)
   {
      $lastlevel = 0;
   }
   else
   {
      $lastlevel = $section_levels{$nodes[$#nodes]->{Type}};
   }

   unless ($currentlevel <= $lastlevel+1)
   {
      die "Node '$attrs{tag}' type can't be deeper than '",
        $section_levels{$lastlevel+1},"' on line ",
        $expat->current_line, "\n";
   }

   my %node = 
   (
      Type      => $attrs{type},
      Title     => $attrs{title},
      Tag       => $attrs{tag},
      Contents  => '',
      Footnotes => '' 
   );

   push @nodes, \%node;

   $tags{$attrs{tag}}->{Ref} = $attrs{tag}.'.html';
   $tags{$attrs{tag}}->{Text} = $attrs{title};

   print TEXFD "\\", $attrs{type}, "{",
      $attrs{title}, "}\\label{",
      $attrs{tag}, "}\n";
}

sub end_node{
   my ($expat) = @_;

}

sub start_newacro{
   my ($expat, %attrs) = @_;

   unless ($attrs{tag})
   {
      die "Missing 'tag' attribute for element 'newacro' on line ",
         $expat->current_line, "\n";
   }

   unless ($attrs{short})
   {
      die "Missing 'short' attribute for element 'newacro' on line ",
         $expat->current_line, "\n";
   }

   unless ($attrs{long})
   {
      die "Missing 'long' attribute for element 'newacro' on line ",
         $expat->current_line, "\n";
   }

   my $tag = $attrs{tag};

   if ($acros{$tag})
   {
      die "Acronym '$tag' already defined\n";
   }

   $acros{$tag}->{Short} = $attrs{short};
   $acros{$tag}->{Long} = $attrs{long};

   # write html file used by popup window

   open FD, ">$helpsetdir/acr_$tag.html" 
      or die "Can't open '$helpsetdir/acr_$tag.html' $!\n";

   print FD &html_head($attrs{short});

   print FD "<body>\n<h1>$attrs{short}</h1>\n",
      ucfirst($attrs{long}), ".\n";

   print FD "</body>\n</html>\n";

   close FD;

   print TEXFD "\\newacronym{$tag}{$attrs{short}}{$attrs{long}}\n";
}

sub end_newacro{
   my ($expat) = @_;
}
 
sub start_newterm{
   my ($expat, %attrs) = @_;

   unless ($attrs{tag})
   {
      die "Missing 'tag' attribute for element 'newterm' on line ",
         $expat->current_line, "\n";
   }

   unless ($attrs{text})
   {
      die "Missing 'text' attribute for element 'newterm' on line ",
         $expat->current_line, "\n";
   }

   my $tag = 'gls_'.$attrs{tag};

   if ($terms{$tag})
   {
      die "Term '", $attrs{tag}, "' already defined\n";
   }

   $terms{$tag}->{Text} = $attrs{text};

   if ($attrs{description})
   {
      $terms{$tag}->{Description} = $attrs{description};

      # write html file used by popup window if a description has been
      # supplied

      open FD, ">$helpsetdir/$tag.html" 
         or die "Can't open '$helpsetdir/$tag.html' $!\n";

      print FD &html_head($attrs{text});

      print FD "<body>\n<h1>$attrs{text}</h1>\n",
         ucfirst($attrs{description}), ".\n";

      print FD "</body>\n</html>\n";

      close FD;

      print TEXFD "\\newglossaryentry{$attrs{tag}}{name={",
       $attrs{text}, "},description={", $attrs{description}, "}}\n";
   }
}

sub end_newterm{
   my ($expat) = @_;
}
 
sub start_p{
   my ($expat, %attrs) = @_;

   my $endtag="\n\n";

   if ($attrs{noindent})
   {
      my $value = lc($attrs{noindent});

      if ($value eq 'true' or $value eq 1)
      {
         $attrs{noindent} = 1;
      }
      elsif ($value eq 'false' or $value eq 0)
      {
         $attrs{noindent} = 0;
      }
      else
      {
         die "Invalid value '$value' of attribute 'noindent' on line ",
           $expat->current_line, "\n";
      }
   }

   if ($attrs{align})
   {
      &handle_html('<p align="'.$attrs{'align'}.'">');

      if ($attrs{align} eq 'left')
      {
         print TEXFD "\\begin{flushleft}";
         $endtag="\\end{flushleft}";
      }
      elsif ($attrs{align} eq 'right')
      {
         print TEXFD "\\begin{flushright}";
         $endtag="\\end{flushright}";
      }
      elsif ($attrs{align} eq 'center')
      {
         print TEXFD "\\begin{center}";
         $endtag="\\end{center}";
      }
      else
      {
         die "Unknown alignment option", $attrs{align},
          " for 'p' element on line ", $expat->current_line, "\n";
      }
   }
   else
   {
      &handle_html("<p>");
      print TEXFD "\n\n";

      print TEXFD "\\noindent " if $attrs{noindent};
   }

   push @paragraph, $endtag;
}

sub end_p{
   my ($expat) = @_;

   &handle_html("</p>");

   print TEXFD pop @paragraph;
}

sub start_dl{
   my ($expat, %attrs) = @_;

   &handle_html("<dl>");

   print TEXFD "\\begin{description}";
}

sub end_dl{
   my ($expat) = @_;

   &handle_html("</dl>");

   print TEXFD "\\end{description}";
}

sub start_ul{
   my ($expat, %attrs) = @_;

   &handle_html("<ul>");

   print TEXFD "\\begin{itemize}";
}

sub end_ul{
   my ($expat) = @_;

   &handle_html("</ul>");

   print TEXFD "\\end{itemize}";
}

sub start_tt{
   my ($expat, %attrs) = @_;

   &handle_html("<tt>");

   print TEXFD "\\texttt{";
}

sub end_tt{
   my ($expat) = @_;

   &handle_html('</tt>');

   print TEXFD "}";
}

sub start_b{
   my ($expat, %attrs) = @_;

   &handle_html("<b>");

   print TEXFD "\\textbf{";
}

sub end_b{
   my ($expat) = @_;

   &handle_html('</b>');

   print TEXFD "}";
}

sub start_em{
   my ($expat, %attrs) = @_;

   &handle_html("<em>");

   print TEXFD "\\emph{";
}

sub end_em{
   my ($expat) = @_;

   &handle_html('</em>');

   print TEXFD "}";
}

sub start_li{
   my ($expat, %attrs) = @_;

   &handle_html("<li>");

   print TEXFD "\\item ";
}

sub end_li{
   my ($expat) = @_;

   &handle_html("</li>");
}


sub start_dt{
   my ($expat, %attrs) = @_;

   &handle_html("<dt>");

   print TEXFD "\\item[{";
}

sub end_dt{
   my ($expat) = @_;

   &handle_html("</dt>");

   print TEXFD "}]";
}

sub start_dd{
   my ($expat, %attrs) = @_;

   &handle_html("<dd>");

}

sub end_dd{
   my ($expat) = @_;

   &handle_html("</dd>");

}

sub start_meta{
   my ($expat, %attrs) = @_;

   &handle_html("&#x27E8;<em>");

   print TEXFD "\\meta{";
}

sub end_meta{
   my ($expat) = @_;

   &handle_html('</em>&#x27E9;');

   print TEXFD "}";
}

sub start_marg{
   my ($expat, %attrs) = @_;

   &handle_html('{');

   print TEXFD "\\marg{";
}

sub end_marg{
   my ($expat) = @_;

   &handle_html('}');

   print TEXFD "}";
}

sub start_dq{
   my ($expat, %attrs) = @_;

   &handle_html('"');

   print TEXFD "\\dq{";
}

sub end_dq{
   my ($expat) = @_;

   &handle_html('"');

   print TEXFD "}";
}

sub start_url{
   my ($expat, %attrs) = @_;

   unless ($attrs{www})
   {
      die "element 'url' missing 'www' attribute\n";
   }

   my $string;

   my $uri = $attrs{www};

   $uri = "http://$uri" unless ($uri=~/^[a-z]+:\/\//);

   if ($attrs{text})
   {
      print TEXFD "\\href{", $attrs{www}, "}{", $attrs{text}, "}";
      $string = "<a href=\"$uri\">$attrs{text}</a>";
   }
   else
   {
      print TEXFD "\\url{", $attrs{www}, "}";
      $string = "<a href=\"$uri\"><tt>$attrs{www}</tt></a>";
   }

   &handle_html($string);
}

sub end_url{
   my ($expat) = @_;
}

sub start_sty{
   my ($expat, %attrs) = @_;

   push @current, 'sty';

   print TEXFD "\\sty{";
}

sub end_sty{
   my ($expat) = @_;

   pop @current;

   my $sty = $docinfo{sty};
   $docinfo{sty} = '';

   &hs_index_term("sty_$sty", "<tt>$sty</tt>", "$sty.sty", \%terms);

   print TEXFD "}";
}

sub start_footnote{
   my ($expat, %attrs) = @_;

   if ($counters{footnote})
   {
      $counters{footnote} = 1;
   }
   else
   {
      $counters{footnote}++;
   }

   $nodes[$#nodes]->{Contents} .=
     "<sup><a href=\"#fn-$counters{footnote}\">$counters{footnote}</a></sup>";

   push @current, 'Footnote';

   print TEXFD "\\footnote{";
}

sub end_footnote{
   my ($expat) = @_;

   pop @current;

   $nodes[$#nodes]->{Footnotes} .=
     "<p><sup><a name=\"fn-$counters{footnote}\">$counters{footnote}</a></sup>"
     . $docinfo{Footnote}
     . '</p>';

   $docinfo{Footnote} = '';

   print TEXFD "}";
}

sub start_example{
   my ($expat, %attrs) = @_;

   if ($counters{example})
   {
      $counters{example}++;
   }
   else
   {
      $counters{example} = 1;
   }

   &handle_html('<blockquote><p><b>'
    . &get_label('manual.example')
    . '&nbsp;' . $counters{example}
    . ($attrs{title}? "($attrs{title})." : '.')
    . '</b></p>');

   print TEXFD "\\begin{example}";

   if ($attrs{title})
   {
      print TEXFD '[', $attrs{title}, ']';
   }
}

sub end_example{
   my ($expat) = @_;

   &handle_html('</blockquote>');

   print TEXFD "\\end{example}";
}

sub start_seealso{
   my ($expat, %attrs) = @_;

   unless ($attrs{tags})
   {
      die "Element 'seealso' missing 'tags' attribute on line ",
        $expat->current_line, "\n";
   }

   my @tags = split /,/, $attrs{tags};

   my $seealso = &get_label('manual.seealso');

   &handle_html("<p>$seealso<ul>");

   print TEXFD "\n\n\\minisec{$seealso}\n";

   print TEXFD "\\begin{itemize}\n";

   foreach my $tag (@tags)
   {
      &handle_html("<li><ref>$tag</ref>");
      print TEXFD "\\item \\nameref{$tag}\n";
   }

   print TEXFD "\\end{itemize}\n";

   &handle_html('</ul>');
}

sub end_seealso{
   my ($expat) = @_;
}

sub start_pre{
   my ($expat, %attrs) = @_;

   &handle_html('<pre>');

   print TEXFD "\\begin{verbatim}";
}

sub end_pre{
   my ($expat) = @_;

   &handle_html('</pre>');

   print TEXFD "\\end{verbatim}";
}

sub start_alltt{
   my ($expat, %attrs) = @_;

   &handle_html('<pre>');

   print TEXFD "\\begin{alltt}";
}

sub end_alltt{
   my ($expat) = @_;

   &handle_html('</pre>');

   print TEXFD "\\end{alltt}";
}

sub start_verbinput{
   my ($expat, %attrs) = @_;

   unless ($attrs{src})
   {
      die "Missing 'verbinput' element's 'src' attribute on line ",
        $expat->current_line, "\n";
   }

   &handle_html('<pre>');

   open FD, $attrs{src} or die "Can't open '$attrs{src}' $!\n";

   while (<FD>)
   {
      s/\&/\&amp;/g;
      s/</\&lt;/g;
      s/>/\&gt;/g;

      &handle_html($_);
   }

   close FD;

   &handle_html('</pre>');

   print TEXFD "\\verbatiminput{$attrs{src}}";
}

sub end_verbinput{
   my ($expat) = @_;
}
 
sub start_verbtabinput{
   my ($expat, %attrs) = @_;

   unless ($attrs{src})
   {
      die "Missing 'verbtabinput' element's 'src' attribute on line ",
        $expat->current_line, "\n";
   }

   &handle_html('<pre>');

   open FD, $attrs{src} or die "Can't open '$attrs{src}' $!\n";

   while (<FD>)
   {
      s/\&/\&amp;/g;
      s/</\&lt;/g;
      s/>/\&gt;/g;

      &handle_html($_);
   }

   close FD;

   &handle_html('</pre>');

   print TEXFD "\\verbatimtabinput{$attrs{src}}";
}

sub end_verbtabinput{
   my ($expat) = @_;
}
 
sub start_float{
   my ($expat, %attrs) = @_;

   unless ($attrs{type})
   {
      die "Element 'float' missing 'type' attribute on line ",
        $expat->current_line, "\n";
   }

   unless ($attrs{tag})
   {
      die "Element 'float' missing 'tag' attribute on line ",
        $expat->current_line, "\n";
   }

   unless ($attrs{caption})
   {
      die "Element 'float' missing 'caption' attribute on line ",
        $expat->current_line, "\n";
   }

   my $pos = ($attrs{pos}? "[$attrs{pos}]" : '');

   if ($counters{$attrs{type}})
   {
      $counters{$attrs{type}}++;
   }
   else
   {
      $counters{$attrs{type}} = 1;
   }

   $attrs{counter} = $counters{$attrs{type}};

   push @float_elements, \%attrs;

   &handle_html("<div class=\"float\"><a name=\"$attrs{tag}\"></a>");

   $tags{$attrs{tag}}->{Ref} = $nodes[$#nodes]->{Tag}
     .'.html#'.$attrs{tag};
   $tags{$attrs{tag}}->{Text} = ucfirst(&get_label('manual.'.$attrs{type}))
        . '&nbsp;' . $attrs{counter};

   unless ($attrs{type} eq 'figure')
   {
      &handle_html('<p></p><p>' . $tags{$attrs{tag}}->{Text} . ': '
        . $attrs{caption} . '</p>');
   }

   print TEXFD "\\begin{", $attrs{type}, "}$pos\n";
   print TEXFD "\\floatconts{", $attrs{tag}, "}{\\caption";

   if ($attrs{listcaption})
   {
      print TEXFD "[", $attrs{listcaption}, "]";
   }

   print TEXFD "{", $attrs{caption}, "}}{";
}

sub end_float{
   my ($expat) = @_;

   my $attrs = pop @float_elements;

   if ($attrs->{type} eq 'figure')
   {
      &handle_html('<p>'. $tags{$attrs->{tag}}->{Text} . ': '
        . $attrs->{caption} . '</p><p></p>');
   }

   &handle_html('</div>');

   print TEXFD "}%\n\\end{", $attrs->{type}, "}\n";
}

sub start_nbsp{
   my ($expat, %attrs) = @_;

   &handle_html('&nbsp;');

   print TEXFD "~";
}

sub end_nbsp{
   my ($expat) = @_;
}

sub start_emdash{
   my ($expat, %attrs) = @_;

   &handle_html('&#x2014;');

   print TEXFD "---";
}

sub end_emdash{
   my ($expat) = @_;
}

sub start_hash{
   my ($expat, %attrs) = @_;

   &handle_html('<tt>#</tt>');

   print TEXFD "\\texttt{\\#}";
}

sub end_hash{
   my ($expat) = @_;
}

sub start_tilde{
   my ($expat, %attrs) = @_;

   &handle_html('~');

   print TEXFD "\\textasciitilde ";
}

sub end_tilde{
   my ($expat) = @_;
}

sub start_lt{
   my ($expat, %attrs) = @_;

   &handle_html('&lt;');

   print TEXFD "\\textless ";
}

sub end_lt{
   my ($expat) = @_;
}

sub start_gt{
   my ($expat, %attrs) = @_;

   &handle_html('&gt;');

   print TEXFD "\\textgreater ";
}

sub end_gt{
   my ($expat) = @_;
}

sub start_element{
   my ($expat, %attrs) = @_;

   &handle_html('<tt>&lt;');

   print TEXFD "\\element{";
}

sub end_element{
   my ($expat) = @_;

   &handle_html('&gt;</tt>');

   print TEXFD "}";
}

sub start_image{
   my ($expat, %attrs) = @_;

   unless ($attrs{src})
   {
      die "Missing 'image' element's 'src' attribute on line ",
        $expat->current_line, "\n";
   }

   my $imgfile = $attrs{src};

   unless (-e $imgfile)
   {
      die "Can't find image file '$imgfile' referenced on line ",
         $expat->current_line, "\n";
   }

   my @opts=();

   my $html = "";

   foreach my $key (keys %attrs)
   {
      if ($key eq 'scale')
      {
         push @opts, "scale=$attrs{scale}";
      }
      elsif ($key eq 'width')
      {
         push @opts, "width=$attrs{width}";
         $html .= "$key=\"$attrs{$key}\" ";
      }
      elsif ($key eq 'height')
      {
         push @opts, "height=$attrs{height}";
         $html .= "$key=\"$attrs{$key}\" ";
      }
      else
      {
         $html .= "$key=\"$attrs{$key}\" ";
      }
   }

   if (not $attrs{width} or not $attrs{height})
   {
      # get image dimensions

      $_ = `file $imgfile`;

      if (/(\d+) x (\d+)/)
      {
         $html .= "width=\"$1\" " unless $attrs{width};
         $html .= "height=\"$2\" " unless $attrs{height};
      }
      else
      {
         warn "Can't determine dimensions of image '$imgfile'\n";
      }
   }

   $html = "<img $html />";

   &handle_html($html);

   print TEXFD "\\includegraphics[",
     join(',', @opts), "]{$imgfile}";
}

sub end_image{
   my ($expat) = @_;
}

sub start_LaTeX{
   my ($expat, %attrs) = @_;

   &handle_html('LaTeX');

   print TEXFD "\\LaTeX{}";
}

sub end_LaTeX{
   my ($expat) = @_;
}

sub start_TeX{
   my ($expat, %attrs) = @_;

   &handle_html('TeX');

   print TEXFD "\\TeX{}";
}

sub end_TeX{
   my ($expat) = @_;
}

sub start_relax{
   my ($expat, %attrs) = @_;

   print TEXFD "\\relax ";
}

sub end_relax{
   my ($expat) = @_;
}

sub start_app{
   my ($expat, %attrs) = @_;

   my $tag="appname";

   my $text = &get_label($tag, \%attrs, $expat);

   &handle_html("<tt>$text</tt>");

   print TEXFD "\\texttt{$text}";
}

sub end_app{
   my ($expat) = @_;
}

sub start_dict{
   my ($expat, %attrs) = @_;

   unless ($attrs{tag})
   {
      die "Missing 'dict' element's 'tag' attribute on line ",
        $expat->current_line, "\n";
   }

   my $text = &get_label($attrs{tag}, \%attrs, $expat);

   &handle_html($text);

   print TEXFD $text;
}

sub end_dict{
   my ($expat) = @_;
}
 
sub start_button{
   my ($expat, %attrs) = @_;

   unless ($attrs{tag})
   {
      die "Missing 'button' element's 'tag' attribute on line ",
        $expat->current_line, "\n";
   }

   my $text = &get_label($attrs{tag}, \%attrs, $expat);

   $text=~s/:\s*$//;

   &handle_html("<b>$text</b>");

   print TEXFD "\\button{$text}";
}

sub end_button{
   my ($expat) = @_;
}
 
sub start_menu{
   my ($expat, %attrs) = @_;

   unless ($attrs{tag})
   {
      die "Missing 'menu' element's 'tag' attribute on line ",
        $expat->current_line, "\n";
   }

   my $hash = \%terms;

   my $currentlabel;
   my $index = '';
   my $html  = '';

   foreach my $item (split /\./, $attrs{tag})
   {
      if ($currentlabel)
      {
         $currentlabel .= ".$item";
         print TEXFD "\\mto ";
         $index .= '!';
         $html .= '&#x279C;';
      }
      else
      {
         $currentlabel = $item;
      }

      my $text = &get_label($currentlabel, \%attrs, $expat);

      $html .= $text;

      print TEXFD "\\menu{$text}";

      my $termtag = 'menu_'.$currentlabel;

      &hs_index_term($termtag, "", $text, $hash);

      unless ($hash->{$termtag}->{Children})
      {
         my %h = ();
         $hash->{$termtag}->{Children} = \%h;
      }

      $hash = $hash->{$termtag}->{Children};

      $text=~s/([@"!|])/"$1/g;

      $index .= "$text@\\menu{$text}";
   }

   &handle_html("<tt>$html</tt>");

   print TEXFD "\\index{$index}";
}

sub end_menu{
   my ($expat) = @_;
}
 
sub start_acr{
   my ($expat, %attrs) = @_;

   unless ($attrs{tag})
   {
      die "Missing 'acr' element's 'tag' attribute on line ",
        $expat->current_line, "\n";
   }

   unless ($acros{$attrs{tag}})
   {
      die "Unknown acronym tag '$attrs{tag}' on line ",
         $expat->current_line, "\n";
   }

   my $html;

   if ($acros{$attrs{tag}}->{Used})
   {
      $html = $acros{$attrs{tag}}->{Short};

      &handle_html(&hs_popup("acr_".$attrs{tag}.".html", $html));
   }
   else
   {
      $acros{$attrs{tag}}->{Used} = 1;

      $html = $acros{$attrs{tag}}->{Long}
            . ' (' . $acros{$attrs{tag}}->{Short} . ')';

      &hs_index_term('acr_'.$attrs{tag}, $html, ucfirst($html), \%terms);
   }

   print TEXFD "\\gls{", $attrs{tag}, "}";
}

sub end_acr{
   my ($expat) = @_;
}
 
sub start_term{
   my ($expat, %attrs) = @_;

   unless ($attrs{tag})
   {
      die "Missing 'term' element's 'tag' attribute on line ",
        $expat->current_line, "\n";
   }

   my $hstag = "gls_".$attrs{tag};

   unless ($terms{$hstag})
   {
      die "Unknown term tag '$hstag' on line ",
         $expat->current_line, "\n";
   }

   my $html = $terms{$hstag}->{Text};

   unless ($terms{$hstag}->{Ref})
   {
      $terms{$hstag}->{Ref} = $nodes[$#nodes]->{Tag} . ".html#$hstag";
   }

   if ($terms{$hstag}->{Description})
   {
      $html = &hs_popup("gls_".$attrs{tag}.".html", $html);

      print TEXFD "\\gls{", $attrs{tag}, "}";
   }
   else
   {
      my $text = $terms{$hstag}->{Text};
      my $idx  = $text;

      $idx=~s/([!@"|])/"$1/g;

      print TEXFD "$text\\index{$idx}";
   }

   &hs_index_term($hstag, $html, $terms{$hstag}->{Text}, 
     \%terms);

}

sub end_term{
   my ($expat) = @_;
}

sub start_ref{
   my ($expat, %attrs) = @_;

   unless ($attrs{tag})
   {
      die "Missing 'ref' element's 'tag' attribute on line ",
        $expat->current_line, "\n";
   }

   &handle_html("<ref>$attrs{tag}</ref>");

   if ($attrs{text})
   {
      print TEXFD "\\hyperref[$attrs{tag}]{$attrs{text}}";
   }
   else
   {
      print TEXFD "\\autoref{", $attrs{tag}, "}";
   }
}

sub end_ref{
   my ($expat) = @_;
}
 
sub start_opt{
   my ($expat, %attrs) = @_;

   unless ($attrs{tag})
   {
      die "Missing 'opt' element's 'tag' attribute on line ",
        $expat->current_line, "\n";
   }

   my $text = '--'.$attrs{tag};

   unless ($terms{options})
   {
      &hs_index_term('options', '', &get_label('manual.options'), \%terms);

      my %hash = ();

      $terms{options}->{Children} = \%hash;
   }

   &hs_index_term('opt_'.$attrs{tag}, "<tt>$text</tt>", $text, 
      $terms{options}->{Children});

   print TEXFD "\\opt{", $attrs{tag}, "}";
}

sub end_opt{
   my ($expat) = @_;
}

sub start_doublebackslash{
   my ($expat, %attrs) = @_;

   &handle_html("<tt>\\\\</tt>");

   print TEXFD "\\cs{\\char`\\\\}";
}

sub end_doublebackslash{
   my ($expat) = @_;
}

sub start_cs{
   my ($expat, %attrs) = @_;

   unless ($attrs{tag})
   {
      die "Missing 'cs' element's 'tag' attribute on line ",
        $expat->current_line, "\n";
   }

   my $text = "\\".$attrs{tag};

   print TEXFD '\\cs{', $attrs{tag}, '}';

   &hs_index_term('cs_'.$attrs{tag}, "<tt>$text</tt>", $text, \%terms);

}

sub end_cs{
   my ($expat) = @_;
}

sub start_env{
   my ($expat, %attrs) = @_;

   unless ($attrs{tag})
   {
      die "Missing 'env' element's 'tag' attribute on line ",
        $expat->current_line, "\n";
   }

   my $text = $attrs{tag};

   print TEXFD '\\env{', $attrs{tag}, '}';

   &hs_index_term('env_'.$attrs{tag}, "<tt>$text</tt>", $text, \%terms);

}

sub end_env{
   my ($expat) = @_;
}

sub start_tabular{
   my ($expat, %attrs) = @_;

   unless ($attrs{spec})
   {
      die "Element 'tabular' missing 'spec' attribute on line ",
         $expat->current_line;
   }

   &handle_html('<table>');

   print TEXFD "\\begin{tabular}{", $attrs{spec}, '}';

   my %hash =
   (
      spec => $attrs{spec},
      column => 1
   );

   push @tabular_specs, \%hash;
}
 
sub end_tabular{
   my ($expat) = @_;

   &handle_html('</table>');

   pop @tabular_specs;

   print TEXFD "\\end{tabular}";
}

sub start_tr{
   my ($expat, %attrs) = @_;

   $tabular_specs[$#tabular_specs]->{column} = 1;

   &handle_html('<tr>');
}

sub end_tr{
   my ($expat) = @_;

   &handle_html('</tr>');

   print TEXFD "\\tabularnewline ";
}

sub start_td{
   my ($expat, %attrs) = @_;

   my $colspan = 1;

   if ($attrs{span})
   {
      $colspan = $attrs{span};

      unless ($colspan=~/^\d+$/)
      {
         die "Attribute 'span' must be an integer on line ",
           $expat->current_line, "\n";
      }
   }

   my $orgcolspec = substr $tabular_specs[$#tabular_specs]->{spec},
      $tabular_specs[$#tabular_specs]->{column}-1, 1;

   my $colspec = $orgcolspec;

   if ($attrs{spec})
   {
      $colspec = $attrs{spec};
   }

   my $htmlcolspan = ($colspan == 1 ? '' : "colspan=\"$colspan\"");

   if ($colspec eq 'l')
   {
      &handle_html("<td align=\"left\" $htmlcolspan>");
   }
   elsif ($colspec eq 'r')
   {
      &handle_html("<td align=\"right\" $htmlcolspan>");
   }
   elsif ($colspec eq 'c')
   {
      &handle_html("<td align=\"center\" $htmlcolspan>");
   }

   unless ($tabular_specs[$#tabular_specs]->{column} == 1)
   {
      print TEXFD "&";
   }

   if (($orgcolspec eq $colspec) and $colspan == 1)
   {
      $tabular_specs[$#tabular_specs]->{egroup} = '';
   }
   else
   {
      print TEXFD "\\multicolumn{$colspan}{$colspec}{";
      $tabular_specs[$#tabular_specs]->{egroup} = '}';
   }

   $tabular_specs[$#tabular_specs]->{column} += $colspan;
}

sub end_td{
   my ($expat) = @_;

   &handle_html('</td>');

   print TEXFD $tabular_specs[$#tabular_specs]->{egroup};
}

sub start_printglossary{
   my ($expat, %attrs) = @_;

   print TEXFD "\\printglossary";
}

sub end_printglossary{
   my ($expat) = @_;
}
 
sub start_printacronyms{
   my ($expat, %attrs) = @_;

   print TEXFD "\\printglossary[type=acronym]";
}

sub end_printacronyms{
   my ($expat) = @_;
}
 
sub start_printindex{
   my ($expat, %attrs) = @_;

   print TEXFD "\\printindex ";
}

sub end_printindex{
   my ($expat) = @_;
}
 
sub start_br{
   my ($expat, %attrs) = @_;

   &handle_html("<br />");

   print TEXFD "\\\\";
}

sub end_br{
   my ($expat) = @_;
}

sub start_hr{
   my ($expat, %attrs) = @_;

   &handle_html("<hr />");

   print TEXFD "\\hrulefill ";
}

sub end_hr{
   my ($expat) = @_;
}

sub start_cont{
   my ($expat, %attrs) = @_;

   print TEXFD "\\continueline ";
}

sub end_cont{
   my ($expat) = @_;
}

sub start_contexplan{
   my ($expat, %attrs) = @_;

  print TEXFD &get_label('manual.cont_explanation');
}


sub end_contexplan{
   my ($expat) = @_;
}

sub start_percent{
   my ($expat, %attrs) = @_;

   &handle_html("%");
   print TEXFD "\\%";
}

sub end_percent{
   my ($expat) = @_;
}

sub html_head{
  my ($title, $prev, $next) = @_;

  my $head = <<_END_HTML_HEAD;
<!DOCTYPE html  html public "-//w3c//dtd xhtml 1.0 Transitional//en"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-GB"
xml:lang="en-GB">
<head>
  <style type="text/css">
    .title
    {
      display: inline;
      font-weight: bold;
      font-size: xx-large;
      text-align: center;
    }
    .author
    {
      display: inline;
      font-weight: bold;
      font-size: large;
      text-align: center;
      padding: 2em;
    }
    .date
    {
      display: inline;
      font-weight: bold;
      font-size: normal;
      text-align: center;
    }
    .float
    {
       text-align: center;
       padding-top: 16;
    }
  </style>
  <title>$title</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
_END_HTML_HEAD

  if ($next)
  {
     $head .= "<link rel=\"next\" href=\"$next.html\" />";
  }

  if ($prev)
  {
     $head .= "<link rel=\"prev\" href=\"$prev.html\" />";
  }

  "$head</head>";
}

sub html_nav{
  my ($prevNode, $nextNode) = @_;

  my $nav = '<hr /><table width="100%"><tr><td align="left">';

  if ($prevNode)
  {
     my $prev;
     my $title;

     if ($prevNode eq $base)
     {
        $prev = $base;
        $title = &get_label('manual.home');
     } 
     else
     {
        $prev = $prevNode->{Tag};
        $title = $prevNode->{Title};
     }

     $nav .= "<a href=\"$prev.html\">&#x21E6; " .
      $title . "</a>";
  }

  $nav .= '</td><td align="right">';

  if ($nextNode)
  {
     $nav .= "<a href=\"$nextNode->{Tag}.html\">"
          . $nextNode->{Title}
          . " &#x21E8;</a>";
  }

  "$nav</td></tr></table>"
}
 
1;
