Output UTF-8; handle empty lines
[project-aon.git] / common / scripts / xmlize.pl
index ad69f76..063bd05 100755 (executable)
@@ -1,77 +1,17 @@
-#!/usr/bin/perl -w
+#!/usr/bin/env perl
 #
 # xmlize.pl
 #
 ######################################################################
 
-#use strict;
+use strict;
+use warnings;
+use utf8;
+use open ':encoding(UTF-8)';
+use open ':std', ':encoding(UTF-8)';
 
-$FILE_EXTENSION = 'txt';
-
-#### Subroutines
-
-sub xmlize {
-    my( $inline, $infile ) = @_;
-
-    $inline =~ s/[[:space:]]*(\.\.\.|\.\s\.\s\.)[[:space:]]*/<ch.ellips\/>/g;
-    $inline =~ tr/\t/ /;
-    $inline =~ s/\s{2,}/ /g;
-    $inline =~ s/\s+$//;
-    $inline =~ s/\&\s/<ch.ampersand\/>/g;
-    $inline =~ tr/\"\`\222\221/\'/;
-    $inline =~ s/(Random\sNumber\sTable)/<a idref=\"random\">$1<\/a>/gi;
-    $inline =~ s/(Action\sCharts?)/<a idref=\"action\">$1<\/a>/gi;
-    # \222 and \221 are some form of funky right and
-    # left quotes not present in ascii (of course) 
-    $inline =~ tr/\227/-/;
-    # \227 is an em or en dash
-
-    $inline =~ s/^\s*(.*)\s*$/$1/;
-
-    if( $inline =~ /^\*/ ) {
-        $inline =~ s/^\*\s*/       <ul>\n        <li>/;
-        $inline =~ s/\s*\*\s*/<\/li>\n        <li>/g;
-        $inline .= "</li>\n       </ul>";
-    }
-    elsif( $inline =~ /^\d+\)\s/ ) {
-        $inline =~ s/^\d+\)\s+/       <ol>\n        <li>/;
-        $inline =~ s/\s*\d+\)\s+/<\/li>\n        <li>/g;
-        $inline .= "</li>\n       </ol>";
-    }
-    elsif( $inline =~ /^\<\!\-\-\spre\s\-\-\>/ ) {
-        $inline =~ s/^\<\!\-\-\spre\s\-\-\>//;
-        warn( "Warning: preformatted text in \"$infile\"\n" );
-    }
-    elsif( $inline =~ /^.+:\s+CLOSE\sCOMBAT\sSKILL/ ) {
-        $inline =~ s/^(.+):\s+CLOSE\sCOMBAT\sSKILL\s+([0-9]+)\s+ENDURANCE\s+([0-9]+)/       <combat><enemy>$1<\/enemy><enemy-attribute class=\"closecombatskill\">$2<\/enemy-attribute><enemy-attribute class=\"endurance\">$3<\/enemy-attribute><\/combat>/g;
-    }
-    elsif( $inline =~ /^.+:\s+COMBAT\sSKILL/ ) {
-        $inline =~ s/^(.+):\s+COMBAT\sSKILL\s+([0-9]+)\s+ENDURANCE\s+([0-9]+)/       <combat><enemy>$1<\/enemy><enemy-attribute class=\"combatskill\">$2<\/enemy-attribute><enemy-attribute class=\"endurance\">$3<\/enemy-attribute><\/combat>/;
-    }
-    elsif( $inline =~ /^(.*)\b(return|turn|go)([a-zA-Z\s]+?to )(\d{1,3})/i ) {
-        $inline =~ s/^(.*)\b(return|turn|go)([a-zA-Z\s]+?to )(\d{1,3})(.*)/       <choice idref=\"sect$4\">$1<link-text>$2$3$4<\/link-text>$5<\/choice>/i;
-        $inline =~ s/\s+<\/choice>/<\/choice>/;
-    }
-    elsif( $inline =~ /^\[/ ) {
-        $inline =~ s/\[(.*)\]/$1/;
-        $inline = "       <signpost>$inline</signpost>";
-        $inline =~ s/\s+<\/signpost>/<\/signpost>/;
-    }
-    elsif( $inline eq "" ) {
-    }
-    elsif( $inline =~ /^<!--(.*)-->/ ) {
-        warn( "Warning: unknown comment \"$1\" in \"$infile\"\n" );
-    }
-    else {
-        $inline = "       <p>$inline</p>";
-        $inline =~ s/\s+<\/p>/<\/p>/;
-    }
-
-# Interferes with selecting a combat paragraph if done earlier
-    $inline =~ s/(COMBAT\sSKILL|CLOSE\sCOMBAT\sSKILL|ENDURANCE|WILLPOWER|\bCS\b|\bEP\b)([^<])/<typ class="attribute">$1<\/typ>$2/g;
-
-    return $inline;
-}
+my $FILE_EXTENSION = 'txt';
+my $BASE_INDENT = '     ';
 
 #### Main Routine
 
@@ -92,7 +32,7 @@ print << "(End of XML Header)";
  %general.inclusions;
 ]>
 
-<gamebook xml:lang="en-UK" version="0.12">
+<gamebook xml:lang="en-UK" version="0.13">
 
  <meta>
   <title>[Insert Title]</title>
@@ -147,22 +87,22 @@ for( my $sectionNumber = $minSectionNumber; $sectionNumber <= $numberOfSections;
 
     foreach my $oldline (@oldlines) {
         $oldline =~ s/\r|\n/ /g;
-       $oldline =~ s/^\s*(\S*)\s*$/$1/;
-       $oldline =~ s/\s\s/ /;
-       if( $oldline ne "" ) {
-           $newline .= (" " . $oldline);
-       }
-       else {
-            $newline = &xmlize( $newline, $infile );
-            $newline .= "\n" if( $newline ne "" );
-           push( @newlines, $newline );
-            $newline = "";
-       }
+        $oldline =~ s/^\s*(\S*)\s*$/$1/;
+        $oldline =~ s/\s{2,}/ /;
+        if( $oldline ne "" ) {
+            $newline .= (" " . $oldline);
+        }
+        else {
+                $newline = &xmlize($newline, $infile);
+                $newline .= "\n" if($newline ne "");
+                push( @newlines, $newline );
+                $newline = "";
+        }
     }
 
-    print "\n\n    <section class=\"numbered\" id=\"sect$sectionNumber\">\n     <meta><title>$sectionNumber</title></meta>\n\n     <data>\n";
+    print "\n\n$BASE_INDENT<section class=\"numbered\" id=\"sect$sectionNumber\">\n$BASE_INDENT <meta><title>$sectionNumber</title></meta>\n\n$BASE_INDENT <data>\n";
     print @newlines;
-    print "     </data>\n    </section>";
+    print "$BASE_INDENT </data>\n$BASE_INDENT</section>";
 }
 
 print << "(End of XML footer)";
@@ -176,3 +116,81 @@ print << "(End of XML footer)";
  </section>
 </gamebook>
 (End of XML footer)
+
+#### Subroutines
+
+sub xmlize {
+    my( $inline, $infile ) = @_;
+
+    if(!defined $inline || $inline eq "") {
+        return "";
+    }
+
+    $inline =~ tr/\t/ /;
+    $inline =~ s/[[:space:]]{2,}/ /g;
+    $inline =~ s/[[:space:]]+$//;
+    $inline =~ s/^[[:space:]]+//;
+    $inline =~ s/[[:space:]]*(\.\.\.|\.\s\.\s\.)[[:space:]]*/<ch.ellips\/>/g;
+
+    $inline =~ s/\&(?=[[:space:]])/<ch.ampersand\/>/g;
+    $inline =~ tr/\"\`/\'/;
+    $inline =~ s/[\N{U+2018}\N{U+201C}]/<quote>/g;
+    $inline =~ s/[\N{U+2019}\N{U+201D}]/<\/quote>/g;
+    $inline =~ s/[\N{U+2014}]/<ch.endash\/>/g;
+    $inline =~ s/[\N{U+2014}]/<ch.emdash\/>/g;
+
+    $inline =~ s/(Random\sNumber\sTable)/<a idref=\"random\">$1<\/a>/gi;
+    $inline =~ s/(Action\sCharts?)/<a idref=\"action\">$1<\/a>/gi;
+
+    if( $inline =~ /^\*/ ) {
+        # unordered lists
+        $inline =~ s/^\*\s*/$BASE_INDENT  <ul>\n$BASE_INDENT   <li>/;
+        $inline =~ s/\s*\*\s*/<\/li>\n$BASE_INDENT   <li>/g;
+        $inline .= "</li>\n$BASE_INDENT  </ul>";
+    }
+    elsif( $inline =~ /^\d+\)\s/ ) {
+        # ordered lists
+        $inline =~ s/^\d+\)\s+/$BASE_INDENT  <ol>\n$BASE_INDENT   <li>/;
+        $inline =~ s/\s*\d+\)\s+/<\/li>\n$BASE_INDENT   <li>/g;
+        $inline .= "</li>\n$BASE_INDENT  </ol>";
+    }
+    elsif( $inline =~ /^\<\!\-\-\spre\s\-\-\>/ ) {
+        # pre-formatted text
+        $inline =~ s/^\<\!\-\-\spre\s\-\-\>//;
+        warn( "Warning: pre-formatted text in \"$infile\"\n" );
+    }
+    elsif( $inline =~ /^.+:\s+CLOSE\sCOMBAT\sSKILL/ ) {
+        # Freeway Warrior combat
+        $inline =~ s/^(.+):\s+CLOSE\sCOMBAT\sSKILL\s+([0-9]+)\s+ENDURANCE\s+([0-9]+)/$BASE_INDENT  <combat><enemy>$1<\/enemy><enemy-attribute class=\"closecombatskill\">$2<\/enemy-attribute><enemy-attribute class=\"endurance\">$3<\/enemy-attribute><\/combat>/g;
+    }
+    elsif( $inline =~ /^.+:\s+COMBAT\sSKILL/ ) {
+        # combat
+        $inline =~ s/^(.+):\s+COMBAT\sSKILL\s+([0-9]+)\s+ENDURANCE\s+([0-9]+)/$BASE_INDENT  <combat><enemy>$1<\/enemy><enemy-attribute class=\"combatskill\">$2<\/enemy-attribute><enemy-attribute class=\"endurance\">$3<\/enemy-attribute><\/combat>/;
+    }
+    elsif( $inline =~ /^(.*)\b(return|turn|go)([a-zA-Z\s]+?to )(\d{1,3})/i ) {
+        # links
+        $inline =~ s/^(.*)\b(return|turn|go)([a-zA-Z\s]+?to )(\d{1,3})(.*)/$BASE_INDENT  <choice idref=\"sect$4\">$1<link-text>$2$3$4<\/link-text>$5<\/choice>/i;
+        $inline =~ s/\s+<\/choice>/<\/choice>/;
+    }
+    elsif( $inline =~ /^\[/ ) {
+        # signposts
+        $inline =~ s/\[(.*)\]/$1/;
+        $inline = "$BASE_INDENT  <signpost>$inline</signpost>";
+        $inline =~ s/\s+<\/signpost>/<\/signpost>/;
+    }
+    elsif( $inline =~ /^<!--(.*)-->/ ) {
+        # comments
+        warn( "Warning: unknown comment \"$1\" in \"$infile\"\n" );
+    }
+    elsif( $inline eq "" ) {
+        # do nothing
+    }
+    else {
+        $inline = "$BASE_INDENT  <p>$inline</p>";
+    }
+
+    # Interferes with selecting a combat paragraph if done earlier
+    $inline =~ s/(COMBAT\sSKILL|CLOSE\sCOMBAT\sSKILL|ENDURANCE|WILLPOWER|\bCS\b|\bEP\b)([^<])/<typ class="attribute">$1<\/typ>$2/g;
+
+    return $inline;
+}