view docs/tfdoc.pl @ 1682:de46e2e23c66

Cleanups.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 01 Apr 2024 23:03:33 +0300
parents acd73fb8402f
children
line wrap: on
line source

#!/usr/bin/perl -w
###
### TFDoc for extending GgrTF DocBook manual with embedded TF docs.
### Programmed by Matti 'ccr' Hamalainen <ccr@tnsp.org>
### (C) Copyright 2009-2024 Tecnic Software productions (TNSP)
###
use strict;
use warnings;
use utf8;


## Convert special characters to HTML/XML entities
sub xmlentities($)
{
  my ($value) = @_;
  $value =~ s/&/&amp;/g;
  $value =~ s/</&lt;/g;
  $value =~ s/>/&gt;/g;
  return $value;
}


sub xmlentities2($)
{
  my ($value) = @_;
  $value =~ s/&/&amp;/g;

  my $str = "";
  my $state = 0;
  foreach my $qch (split(//, $value))
  {
    if ($qch eq "\$")
    {
      $state = !$state;
      $str .= ($state ? "<emphasis>" : "</emphasis>");
    }
    else
    {
      $str .= $qch;
    }
  }
  return $str;
}


### Scan one TinyFugue script file for documentation entries
sub scan_file($)
{
  my ($filename) = @_;

  print STDERR "Scanning '".$filename."'\n";
  my $data = {};
  my $cmd = "";
  my $sect = "";
  my %bindtypes = ("c" => "cast", "g" => "general", "s" => "skill");

  open(my $fh, "<:encoding(iso-8859-1)", $filename) or die("Could not open '".$filename."' for reading.\n");
  while (defined(my $line = <$fh>))
  {
    chomp($line);
    if ($line =~ /\/prdef(c|g|b)bind\s+-s\"(.+?)\"\s+-c\"(.+?)\"\s*(.*)$/)
    {
      my $opts = $4;
      my $type = $bindtypes{$1};
      my $tmp = {};
      $tmp->{"name"} = $2;
      $tmp->{"desc"} = $3;
      $tmp->{"quiet"} = ($opts =~ /-q/) ? 1 : 0;
      $tmp->{"notarget"} = ($opts =~ /-n/) ? 1 : 0;
      push(@{$data->{"binds"}{$type}}, $tmp);
    }
    elsif ($line =~ /;\s*\@keybind\s+(.*?)\s*=\s*(.*?)$/)
    {
      $data->{"keybinds"}{$1} = $2;
    }
    elsif ($line =~ /;\s*\@command\s+(\S+)\s*?(.*)$/)
    {
      $cmd = $1;
      $sect = "commands";
      $data->{$sect}{$cmd}{"opts"} = $2;
    }
    elsif ($line =~ /;\s*\@desc\s+(.*)$/)
    {
      if ($sect ne "" && $cmd ne "")
      {
        $data->{$sect}{$cmd}{"desc"} .= $1." ";
      }
    }
  }

  close($fh);
  return $data;
}


### Print out a DocBook SGML/XML table header
sub table_start
{
  my $title = shift;
  my $cols = shift;
  
  print
    "<table>\n".
    " <title>".$title."</title>\n".
    " <tgroup cols=\"".$cols."\" align=\"left\">\n".
    "  <thead>\n".
    "   <row>\n";
  
  foreach my $col (@_)
  {
    print "    <entry>".$col."</entry>\n";
  }
  
  print
    "   </row>\n".
    "  </thead>\n".
    "  <tbody>\n";
}

sub table_end
{
  print
    "  </tbody>\n".
    " </tgroup>\n".
    "</table>\n";
}


sub handle_directive($$$$$)
{
  my ($mode, $title, $currfile, $files, $linen) = @_;

  die("Directive '\@".$mode." ". $title."' found, but no \@file directive set before it on line ".$linen.".\n")
    unless defined($currfile);

  if ($mode eq "keybinds")
  {
    # Keyboard bindings
    if (defined($files->{$currfile}{"keybinds"}))
    {
      my $data = $files->{$currfile}{"keybinds"};
      table_start((defined($title) ? xmlentities($title)." k" : "K")."eybindings", "2", "Key(s)", "Function");
      foreach my $tmp (sort keys %{$data})
      {
        print
          "   <row><entry>".xmlentities($tmp)."</entry>".
          "<entry>".xmlentities($data->{$tmp})."</entry></row>\n";
      }
      table_end();
    }
  }
  elsif ($mode eq "binds")
  {
    # Command bindings
    if (defined($files->{$currfile}{"binds"}))
    {
      my $data = $files->{$currfile}{"binds"};
      foreach my $type (sort keys %{$data})
      {
        my $entry = $data->{$type};
        table_start((defined($title) ? xmlentities($title)." " : "")."'".$type."' type command bindings", "4", "Command", "Quiet", "NoTarget", "Description");
        foreach my $entry (sort @{$data->{$type}})
        {
          print
            "   <row><entry>".xmlentities($entry->{"name"})."</entry>".
            "<entry>".($entry->{"quiet"} ? "X" : "")."</entry>".
            "<entry>".($entry->{"notarget"} ? "X" : "")."</entry>".
            "<entry>".xmlentities($entry->{"desc"})."</entry></row>\n";
        }
        table_end();
      }
    }
  }
  elsif ($mode eq "commands")
  {
    # Macro commands
    if (defined($files->{$currfile}{"commands"}))
    {
      my $data = $files->{$currfile}{"commands"};
      
      table_start((defined($title) ? xmlentities($title)." m" : "M")."acro commands", "2", "Command", "Description");
      foreach my $tmp (sort keys %{$data})
      {
        print
          "   <row><entry><emphasis>".xmlentities($tmp)."</emphasis> ".xmlentities($data->{$tmp}{"opts"})."</entry>".
          "<entry>".xmlentities2($data->{$tmp}{"desc"}).
          "</entry></row>\n";
      }
      table_end();
    }
  }
  else
  {
    die("Invalid/unsupported directive '\@".$mode." ".$title."' on line ".$linen.".\n");
  }
}


###
### Main program
###
my $basepath = shift or die("Usage: <tfdir basepath> < input.sgml > output.sgml\n");

print STDERR "Using TF-basepath '".$basepath."'\n";

binmode(STDOUT, ":utf8");
binmode(STDIN, ":utf8");

my $linen = 0;
my ($currfile, $currtitle);
my $files = {};

while (defined (my $line = <STDIN>))
{
  $linen++;

  # Get module filenames from section titles
  if ($line =~ /<title>(.*?)\((\S+?\.tf)\)<\/title>/)
  {
    print $line;
    $currtitle = $1;
    $currfile = $2;
    if (!defined($files->{$currfile}))
    {
      $files->{$currfile} = scan_file($basepath.$currfile);
    }
  }
  elsif ($line =~ /<!--\s*\@file\s+\"(.+?)\"\s+\"(.+?)\"\s*-->/)
  {
    $currfile = $1;
    $currtitle = $2;
    if (!defined($files->{$currfile}))
    {
      $files->{$currfile} = scan_file($basepath.$currfile);
    }
  }
  elsif ($line =~ /<!--\s*\@([a-z]+)\s+\"(.+?)\"\s*-->/)
  {
    handle_directive($1, $2, $currfile, $files, $linen);
  }
  elsif ($line =~ /<!--\s*\@([a-z]+)\s*-->/)
  {
    handle_directive($1, $currtitle, $currfile, $files, $linen);
  }
  else
  {
    print $line;
  }
}