chg: mention CVE for 3.97.1
[exim-website.git] / script / gen
index dfb174eeb52c488ace69f0188211b349732617b1..58e399e8477376457e87a800a2ade71881fa78d6 100755 (executable)
@@ -15,6 +15,7 @@ use JavaScript::Minifier::XS;
 use Pod::Usage;
 use XML::LibXML;
 use XML::LibXSLT;
+use FindBin qw'$Bin';
 
 my $canonical_url = 'https://www.exim.org/';
 
@@ -130,11 +131,21 @@ sub do_doc {
     my ( $type, $xml_path ) = @_;
 
     ## Read and validate the XML file
-    my $xml = XML::LibXML->new()->parse_file($xml_path) or die $!;
+    my $xml = XML::LibXML->new(expand_entities => 1)->parse_file($xml_path) or die $!;
 
     ## Get the version number
-    my $version = $xml->findvalue('/book/bookinfo/revhistory/revision/revnumber');
-    die "Unable to get version number\n" unless defined $version && $version =~ /^\d+(\.\d+)*$/;
+    my $version = do {
+       my $version = $xml->findvalue('/book/bookinfo/revhistory/revision/revnumber');
+       die "Unable to get version number\n"
+           unless defined $version and $version =~ /^
+               (?<version>
+                   \d+                     # major
+                   (?:\.\d+(?:\.\d+)?)?    # (minor(.patch))
+                   (?:\.\d+(?:\.\d+(?:\.\d+)?)?)?    # (minor(.patch.(fixes)))
+               )
+               (?:-RC\d+)?$/x;             # -RCX
+       $+{version};
+    };
 
     ## Prepend chapter filenames?
     my $prepend_chapter = $type eq 'filter' ? 'filter_' : '';
@@ -276,6 +287,7 @@ sub xref_fixup {
         my $section_counter = 0;
         foreach my $section ( $chapter->findnodes('section') ) {
             ++$section_counter;
+           $section->setAttribute( 'sectprefix', $section_counter );
 
             my $section_id = $section->getAttribute('id');
             unless ($section_id) {    # synthesise missing id
@@ -290,6 +302,31 @@ sub xref_fixup {
                 section_id    => $section_counter,
                 section_title => $section_title
             };
+
+           # 2022/07/07 jgh: added loop for sections under sections, which are resulting from the .subsection macro
+           # Add a "level" attribute to these nodes
+           ## Iterate over each subsection
+           my $subsec_counter = 0;
+           foreach my $subsection ( $section->findnodes('section') ) {
+             ++$subsec_counter;
+
+             $subsection->setAttribute( 'level', "2" );
+             $subsection->setAttribute( 'sectprefix', sprintf("%d.%d", $section_counter, $subsec_counter) );
+
+             my $subsec_id = $subsection->getAttribute('id');
+             unless ($subsec_id) {    # synthesise missing id
+                 $subsec_id = sprintf( 'section_noid_%04d_%04d_%04d', $chapter_counter, $section_counter, $subsec_counter );
+                 $subsection->setAttribute( 'id', $subsec_id );
+             }
+             my $subsec_title = $subsection->findvalue('title');
+
+             $index{$subsec_id} = {
+                 chapter_id    => $chapter_counter,
+                 chapter_title => $chapter_title,
+                 section_id    => $subsec_counter,
+                 section_title => $subsec_title
+             };
+           }
         }
     }
     ## Build indexes as new chapters
@@ -298,10 +335,11 @@ sub xref_fixup {
     ## Replace all of the xrefs in the XML
     foreach my $xref ( $xml->findnodes('//xref') ) {
         my $linkend = $xref->getAttribute('linkend');
+
         if ( exists $index{$linkend} ) {
-            $xref->setAttribute( 'chapter_id',    $index{$linkend}{'chapter_id'} );
+            $xref->setAttribute( 'chapter_id',    $index{$linkend}{'chapter_id'} ) if ( $index{$linkend}{'chapter_id'} );
             $xref->setAttribute( 'chapter_title', $index{$linkend}{'chapter_title'} );
-            $xref->setAttribute( 'section_id', $index{$linkend}{'section_id'} ) if ( $index{$linkend}{'section_id'} );
+            $xref->setAttribute( 'section_id',   $index{$linkend}{'section_id'} ) if ( $index{$linkend}{'section_id'} );
             $xref->setAttribute( 'section_title', $index{$linkend}{'section_title'} )
                 if ( $index{$linkend}{'section_title'} );
             $xref->setAttribute( 'url',
@@ -317,17 +355,73 @@ sub build_indexes {
     my ( $xml, $prepend_chapter, $xref ) = @_;
 
     my $index_hash = {};
+    my $seealso_hash = {};
     my $current_id;
-    foreach my $node ( $xml->findnodes('//section | //chapter | //indexterm') ) {
+    my $verterm_counter = 0;
+
+    foreach my $node ( $xml->findnodes('//section | //chapter | //varlistentry | //indexterm') ) {
         if ( $node->nodeName eq 'indexterm' ) {
             my $role      = $node->getAttribute('role') || 'concept';
             my $primary   = $node->findvalue('child::primary');
             my $first     = ( $primary =~ /^[A-Za-z]/ ) ? uc( substr( $primary, 0, 1 ) ) : '';  # first letter or marker
             my $secondary = $node->findvalue('child::secondary') || '';
+           my $see       = $node->findvalue('child::see');
+           my $see_also  = $node->findvalue('child::seealso');
+
             next unless ( $primary || $secondary );    # skip blank entries for now...
+
             $index_hash->{$role}{$first}{$primary}{$secondary} ||= [];
-            push @{ $index_hash->{$role}{$first}{$primary}{$secondary} }, $current_id;
+           if ( $see || $see_also ) {
+             # The scalar value being written here assumes only one seealso on an indeed term
+             # It would be nice to have the $see displayed in bold rather than in quotes
+             $seealso_hash->{$role}{$first}{$primary}{$secondary} = 'see "' . $see .'"' if ($see);
+             $seealso_hash->{$role}{$first}{$primary}{$secondary} = 'see also "' . $see_also .'"' if ($see_also);
+           }
+
+           else {
+               push @{ $index_hash->{$role}{$first}{$primary}{$secondary} }, $current_id;
+           }
         }
+       elsif ( $node->nodeName eq 'varlistentry' ) {
+
+         foreach my $vitem ( $node->findnodes('listitem') ) {
+
+           # Add an anchorname xml attribute.
+           # chapter.xsl spots this and places a "<a id="{@anchorname}"> </a>"
+
+           my $anchorname = sprintf("vi%d", $verterm_counter++);
+           $vitem->setAttribute( 'anchorname', $anchorname );
+           $current_id = $anchorname;
+
+           # Set the latest indexable id to be picked up by the next indexterm,
+           # which should be in the content of the listitem
+
+           my ($chapter_title, $sec_id, $sec_title);
+
+           foreach my $chap ( $node->findnodes('ancestor::chapter') ) {
+             $chapter_title = $chap->findvalue('title');
+           }
+           next unless ($chapter_title);
+
+           # Search upward to find a subsection or section id & title
+           foreach my $ssec ( $node->findnodes("ancestor::section[\@level='2']") ) {
+             $sec_id = $ssec->getAttribute('id');
+             $sec_title = $ssec->findvalue('title');
+             last;
+           }
+           if (!defined($sec_id)) {
+             foreach my $sec ( $node->findnodes('ancestor::section') ) {
+               $sec_id = $sec->getAttribute('id');
+               $sec_title = $sec->findvalue('title');
+               last;
+             }
+           }
+
+           $xref->{$anchorname}{'chapter_title'} = $chapter_title;
+           $xref->{$anchorname}{'section_id'} = $anchorname;
+           $xref->{$anchorname}{'section_title'} = $sec_title if ($sec_title);
+         }
+       }
         else {
             $current_id = $node->getAttribute('id');
         }
@@ -372,6 +466,10 @@ sub build_indexes {
                         $slist->appendChild($sentry)->appendTextChild( 'term', $secondary );
                         $sentry->appendChild($sitem)->appendChild($para);
                     }
+
+                   my $seealso = $seealso_hash->{$role}{$first}{$primary}{$secondary};
+                   $para->appendText($seealso) if ($seealso);
+
                     my $count = 0;
                     foreach my $ref ( @{ $index_hash->{$role}{$first}{$primary}{$secondary} } ) {
                         $para->appendText(', ')
@@ -408,7 +506,7 @@ sub transform {
     $xml->documentElement()->appendTextChild( 'old_versions', $_ ) foreach old_docs_versions();
 
     ## Parse the ".xsl" file as XML
-    my $xsl = XML::LibXML->new()->parse_file($xsl_path) or die $!;
+    my $xsl = XML::LibXML->new(expand_entities => 1)->parse_file($xsl_path) or die $!;
 
     ## Generate a stylesheet from the ".xsl" XML.
     my $stylesheet = XML::LibXSLT->new()->parse_stylesheet($xsl);
@@ -464,7 +562,7 @@ sub error_help {
 ## Parse arguments
 sub parse_arguments {
 
-    my %opt = ( spec => [], filter => [], help => 0, man => 0, web => 0, minify => 1, verbose => 0, localstatic => 0 );
+    my %opt = ( spec => [], filter => [], help => 0, man => 0, web => 0, minify => 1, verbose => 0, localstatic => 0, tmpl => "$Bin/../templates" );
     GetOptions(
         \%opt,      'help|h!', 'man!',      'web!',    'spec=s{1,}', 'filter=s{1,}',
         'latest=s', 'tmpl=s',  'docroot=s', 'minify!', 'verbose!',   'localstatic!'
@@ -506,11 +604,11 @@ __END__
 
 =head1 NAME
 
-gen.pl - Generate exim html documentation and website
+gen - Generate exim html documentation and website
 
 =head1 SYNOPSIS
 
-gen.pl [options]
+gen [options]
 
  Options:
    --help              display this help and exits
@@ -590,7 +688,7 @@ Generates the exim website and HTML documentation.
 
 =head1 EXAMPLE
 
-    script/gen.pl \
+    script/gen \
       --web \
       --spec docbook/*/spec.xml \
       --filter  docbook/*/filter.xml \