Changed the 03oz abbreviation to 03toz
[project-aon.git] / scripts / corrtohtml.pl
1 #!/usr/bin/perl -w
2
3 use strict;
4
5 my $programName = 'corrtohtml';
6 my $usage = "$programName [options] [inputFile [inputFile2 ...]]\n" .
7             "\t-b bookCode          convert unspecified corrections to this book\n" .
8             "\t-o outputFile\n" .
9             "\t-i editorsInitials\n" .
10
11             "\t-s                   strips book information\n" .
12             "\t-v                   verbose reporting\n";
13
14 my $optsProcessed = 0;
15 my $outFile = "";
16 my $editorInitials = "";
17 my $stripBookInfo = 0;
18 my $verbose = 0;
19 my $bookCode = "";
20 my $bookCodeReport = "";
21
22 while( $#ARGV > -1 && not $optsProcessed ) {
23   my $commandLineItem = shift @ARGV;
24   if( $commandLineItem eq "-b" ) {
25     $bookCode = shift @ARGV or die $usage;
26     &validateBookCode( $bookCode ) or die( "Error ($programName): unrecognized bookcode on command line \"$bookCode\"" );
27   }
28   elsif( $commandLineItem eq "-o" ) {
29     $outFile = shift @ARGV or die $usage;
30   }
31   elsif( $commandLineItem eq "-i" ) {
32     $editorInitials = shift @ARGV or die $usage;
33   }
34   elsif( $commandLineItem eq "-s" ) {
35     $stripBookInfo = 1;
36   }
37   elsif( $commandLineItem eq "-v" ) {
38     $verbose = 1;
39   }
40   elsif( $commandLineItem eq "--help" ) {
41     print $usage and exit;
42   }
43   else {
44     unshift @ARGV, $commandLineItem;
45     $optsProcessed = 1;
46   }
47 }
48
49 my @lines = <>;
50 my $document = "";
51 my %sectionDocLookup = (
52   '_unknown' => '_unknown',
53   'toc'      => 'toc',
54   'title'    => 'title',
55   'dedicate' => 'dedicate',
56   'acknwldg' => 'acknwldg',
57   'credits'  => 'acknwldg',
58   'coming'   => 'coming',
59   'tssf'     => 'tssf',
60   'gamerulz' => 'gamerulz',
61   'discplnz' => 'discplnz',
62   'camflage' => 'discplnz',
63   'hunting'  => 'discplnz',
64   'sixthsns' => 'discplnz',
65   'tracking' => 'discplnz',
66   'healing'  => 'discplnz',
67   'wepnskll' => 'discplnz',
68   'mndshld'  => 'discplnz',
69   'mndblst'  => 'discplnz',
70   'anmlknsp' => 'discplnz',
71   'mindomtr' => 'discplnz',
72   'mksumary' => 'discplnz',
73   'anmlctrl' => 'discplnz',
74   'curing'   => 'discplnz',
75   'invsblty' => 'discplnz',
76   'psisurge' => 'discplnz',
77   'psiscrn'  => 'discplnz',
78   'dvnation' => 'discplnz',
79   'wpnmstry' => 'discplnz',
80   'anmlmstr' => 'discplnz',
81   'deliver'  => 'discplnz',
82   'assimila' => 'discplnz',
83   'hntmstry' => 'discplnz',
84   'pthmnshp' => 'discplnz',
85   'kaisurge' => 'discplnz',
86   'kaiscrn'  => 'discplnz',
87   'nexus'    => 'discplnz',
88   'gnosis'   => 'discplnz',
89   'magi'     => 'discplnz',
90   'kalchemy' => 'discplnz',
91   'powers'   => 'powers',
92   'lessmcks' => 'powers',
93   'alchemy'  => 'powers',
94   'sorcery'  => 'powers',
95   'enchant'  => 'powers',
96   'elementl' => 'powers',
97   'prophecy' => 'powers',
98   'psycmncy' => 'powers',
99   'evcation' => 'powers',
100   'highmcks' => 'powers',
101   'thamtrgy' => 'powers',
102   'telergy'  => 'powers',
103   'physirgy' => 'powers',
104   'theurgy'  => 'powers',
105   'visionry' => 'powers',
106   'necrmncy' => 'powers',
107   'staff'    => 'powers',
108   'moonston' => 'powers',
109   'equipmnt' => 'equipmnt',
110   'howcarry' => 'equipmnt',
111   'howmuch'  => 'equipmnt',
112   'howuse'   => 'equipmnt',
113   'cmbtrulz' => 'cmbtrulz',
114   'evasion'  => 'cmbtrulz',
115   'lorecrcl' => 'lorecrcl',
116   'lcbonus'  => 'lorecrcl',
117   'levels'   => 'levels',
118   'primate'  => 'levels',
119   'tutelary' => 'levels',
120   'mentora'  => 'levels',
121   'scion'    => 'levels',
122   'archmstr' => 'levels',
123   'prncpln'  => 'levels',
124   'imprvdsc' => 'imprvdsc',
125   'guardian' => 'imprvdsc',
126   'sunkght'  => 'imprvdsc',
127   'sunlord'  => 'imprvdsc',
128   'kaiwisdm' => 'kaiwisdm',
129   'sage'     => 'sage',
130   'numbered' => 'numbered',
131   'part1'    => 'part1',
132   'part2'    => 'part2',
133   'ill1'     => 'ill1',
134   'ill2'     => 'ill2',
135   'ill3'     => 'ill3',
136   'ill4'     => 'ill4',
137   'ill5'     => 'ill5',
138   'ill6'     => 'ill6',
139   'ill7'     => 'ill7',
140   'ill8'     => 'ill8',
141   'ill9'     => 'ill9',
142   'ill10'    => 'ill10',
143   'ill11'    => 'ill11',
144   'ill12'    => 'ill12',
145   'ill13'    => 'ill13',
146   'ill14'    => 'ill14',
147   'ill15'    => 'ill15',
148   'ill16'    => 'ill16',
149   'ill17'    => 'ill17',
150   'ill18'    => 'ill18',
151   'ill19'    => 'ill19',
152   'ill20'    => 'ill20',
153   'passing'  => 'passing',
154   'map'      => 'map',
155   'action'   => 'action',
156   'crsumary' => 'crsumary',
157   'smevazn'  => 'crsumary',
158   'crtable'  => 'crtable',
159   'random'   => 'random',
160   'errata'   => 'errata',
161   'errintro' => 'errata',
162   'errerr'   => 'errata',
163   'footnotz' => 'footnotz',
164   'illstrat' => 'illstrat',
165   'primill'  => 'illstrat',
166   'secill'   => 'illstrat',
167   'license'  => 'license',
168   'lic-pre'  => 'license',
169   'lic-1'    => 'license',
170   'lic-1-0'  => 'license',
171   'lic-1-1'  => 'license',
172   'lic-1-2'  => 'license',
173   'lic-1-3'  => 'license',
174   'lic-1-4'  => 'license',
175   'lic-1-5'  => 'license',
176   'lic-1-6'  => 'license',
177   'lic-1-7'  => 'license',
178   'lic-2'    => 'license',
179   'lic-2-0'  => 'license',
180   'lic-2-1'  => 'license',
181   'lic-2-2'  => 'license',
182   'lic-2-3'  => 'license',
183   'lic-2-4'  => 'license',
184   'lic-2-5'  => 'license',
185   'lic-3'    => 'license',
186   'lic-3-0'  => 'license',
187   'lic-3-1'  => 'license',
188   'lic-4'    => 'license',
189   'lic-4-0'  => 'license',
190   'lic-5'    => 'license',
191   'lic-5-0'  => 'license',
192   'lic-6'    => 'license',
193   'lic-6-0'  => 'license',
194   'lic-6-1'  => 'license'
195 );
196
197 my %sectionTitleLookup = (
198   '_unknown' => '_unknown',
199   'toc'      => 'Table of Contents',
200   'title'    => 'Title Page',
201   'dedicate' => 'Dedication',
202   'acknwldg' => 'Acknowledgements',
203   'coming'   => 'Of the Coming of Grey Star',
204   'tssf'     => 'The Story So Far . . .',
205   'gamerulz' => 'The Game Rules',
206   'discplnz' => '. . . Disciplines',
207   'powers'   => 'Magical Powers',
208   'equipmnt' => 'Equipment',
209   'cmbtrulz' => 'Rules for Combat',
210   'lorecrcl' => 'Lore-circles of the Magnakai',
211   'levels'   => 'Levels of . . . Mastery',
212   'imprvdsc' => 'Improved . . . Disciplines',
213   'kaiwisdm' => '. . . Wisdom',
214   'sage'     => 'Sage Advice',
215   'numbered' => 'Numbered Sections',
216   'part1'    => 'Part I',
217   'part2'    => 'Part II',
218   'ill1'     => 'Illustration 1',
219   'ill2'     => 'Illustration 2',
220   'ill3'     => 'Illustration 3',
221   'ill4'     => 'Illustration 4',
222   'ill5'     => 'Illustration 5',
223   'ill6'     => 'Illustration 6',
224   'ill7'     => 'Illustration 7',
225   'ill8'     => 'Illustration 8',
226   'ill9'     => 'Illustration 9',
227   'ill10'    => 'Illustration 10',
228   'ill11'    => 'Illustration 11',
229   'ill12'    => 'Illustration 12',
230   'ill13'    => 'Illustration 13',
231   'ill14'    => 'Illustration 14',
232   'ill15'    => 'Illustration 15',
233   'ill16'    => 'Illustration 16',
234   'ill17'    => 'Illustration 17',
235   'ill18'    => 'Illustration 18',
236   'ill19'    => 'Illustration 19',
237   'ill20'    => 'Illustration 20',
238   'passing'  => 'Passing of the Shianti',
239   'map'      => 'map',
240   'action'   => 'Action Chart',
241   'crsumary' => 'Combat Rules Summary',
242   'crtable'  => 'Combat Results Table',
243   'random'   => 'Random Number Table',
244   'errata'   => 'Errata',
245   'footnotz' => 'Footnotes',
246   'illstrat' => 'Table of Illustrations',
247   'license'  => 'Project Aon License'
248 );
249
250 if( $bookCode ne "" ) {
251     $bookCodeReport = " [$bookCode]";
252 }
253 ################################################################################
254 # Normalize Lines and Whitespace
255
256 foreach my $line (@lines) {
257   $line =~ tr/\n\r/ /;
258   $document .= $line;
259 }
260 $document =~ s/[[:space:]]{2,}/ /g;                        # collapse spaces
261 $document =~ s/(\(er?\)|\(ne?\)|\(ft?\)|\(ce\)|\(cn\)|\(cf\)|\(re\)|\(rn\)|\(rf\)|\(\?\??\))/\n$1/g; # break lines
262 $document =~ s/^[[:space:]]*\n//g;                         # remove blank lines
263 @lines = split( m/ *\n/, $document );
264
265 ################################################################################
266 # Translate
267
268 my $commentRegex       = qr{\[[[:space:]]*(([^[:space:]:]*)[[:space:]]*:)?[[:space:]]*([^]]*)\]};
269 my $sectionNumberRegex = qr{^\(([^)][^)])*\)                       # type: $1
270                             [[:space:]]*
271                             ([[:digit:]]*[[:alpha:]]+[[:space:]]+)? # book: $2
272                             ([[:digit:]]+)                          # section: $3
273                             (?:[[:space:]]+
274                             \#([[:digit:]]+))?                      # issue: $4
275                             [[:space:]]*:
276                             (.*?)                                   # correction: $5
277                             [[:space:]]*$}x;
278 my $sectionIDRegex     = qr{^\(([^)][^)])*\)                        # type: $1
279                             [[:space:]]*
280                             ([[:digit:]]*[[:alpha:]]+[[:space:]]+)? # book: $2
281                             ([^:[:space:]]*)                        # section: $3
282                             (?:[[:space:]]+
283                             \#([[:digit:]]+))?                      # issue: $4
284                             [[:space:]]*:
285                             (.*?)                                   # correction: $5
286                             [[:space:]]*$}x;
287
288 foreach my $line (@lines) {
289   $line =~ s{&} {&amp;}g;  # escape for HTML
290   $line =~ s{<} {&lt;}g;   #   "
291   $line =~ s{>} {&gt;}g;   #   "
292
293   while( $line =~ m{$commentRegex} ) {
294     if( (not defined( $2 )) || $2 eq "" ) {
295       $line =~ s{$commentRegex}{<div class="cm $editorInitials">$3</div>};
296     } else {
297       my $initials = lc( $2 );
298       $line =~ s{$commentRegex}{<div class="cm $initials">$3</div>};
299     }
300     if( $3 =~ m/^[[:space:]]*$/ ) {
301       warn( "Warning ($programName)$bookCodeReport: empty comment found\n" );
302     }
303   }
304
305   if( $line =~ m{$sectionNumberRegex} ) {
306     my $book = "";
307     if( defined $2 ) {
308       $book = lc( $2 );
309       &validateBookCode( $book ) or die( "Error ($programName)$bookCodeReport: unrecognized bookcode in input corrections \"$book\"" );
310     }
311     elsif( $bookCode ) {
312       $book = $bookCode;
313       warn( "Warning ($programName)$bookCodeReport: entry with unspecified book coerced to $bookCode: $line\n" );
314     }
315
316     my $issue = "";
317     if( defined $4 ) { $issue = $4; }
318     my $caseFoldSection = lc( $3 );
319     if( $book ne "" && not $stripBookInfo ) {
320       $line =~ s{$sectionNumberRegex}{<div class="$1"><!-- $book--><a href="sect$caseFoldSection.htm">$caseFoldSection</a> #$issue:$5</div>\n};
321     }
322     else {
323       $line =~ s{$sectionNumberRegex}{<div class="$1"><a href="sect$caseFoldSection.htm">$caseFoldSection</a> #$issue:$5</div>\n};
324     }
325   }
326   elsif( $line =~ m{$sectionIDRegex} ) {
327     my $caseFoldSection = lc( $3 );
328     exists $sectionDocLookup{$caseFoldSection} && defined $sectionDocLookup{$caseFoldSection}
329       or die( "Error ($programName)$bookCodeReport: don\'t understand section ID \"$caseFoldSection\" in $line" );
330     exists $sectionTitleLookup{$sectionDocLookup{$caseFoldSection}} && defined $sectionTitleLookup{$sectionDocLookup{$caseFoldSection}}
331       or die( "Error ($programName)$bookCodeReport: section ID \"$caseFoldSection\" doesn\'t have an associated title" );
332
333     my $book = "";
334     if( defined $2 ) {
335       $book = $2;
336       chomp( $book );
337       &validateBookCode( $book ) or die( "Error ($programName)$bookCodeReport: unrecognized bookcode in input corrections \"$book\"" );
338     }
339     elsif( $bookCode ) {
340       $book = $bookCode;
341       warn( "Warning ($programName)$bookCodeReport: entry with unspecified book coerced to $bookCode: $line\n" );
342     }
343
344     my $issue = "";
345     if( defined $4 ) { $issue = $4; }
346
347     if( $book ne "" && not $stripBookInfo ) {
348       $line =~ s{$sectionIDRegex}{<div class="$1"><!-- $book--><a href="$sectionDocLookup{$caseFoldSection}.htm">$sectionTitleLookup{$sectionDocLookup{$caseFoldSection}}</a> \#$issue:$5</div>\n};
349     }
350     else {
351       $line =~ s{$sectionIDRegex}{<div class="$1"><a href="$sectionDocLookup{$caseFoldSection}.htm">$sectionTitleLookup{$sectionDocLookup{$caseFoldSection}}</a> \#$issue:$5</div>\n};
352     }
353   }
354   else {
355     die( "Error ($programName)$bookCodeReport: unable to parse line: $line\n" );
356   }
357
358   $line =~ s{class="\?\??"} {class="u"};
359   $line =~ s{class="er"} {class="e"};
360   $line =~ s{class="ne"} {class="n"};
361   $line =~ s{class="ft"} {class="f"};
362
363   if( $line =~ m/(\(.{,4}\))|(\[.{,4}\])/ ) {
364     warn( "Warning ($programName)$bookCodeReport: possible malformed correction entry: $line\n" );
365   }
366 }
367
368 ################################################################################
369 # Output Results
370
371 if( $outFile ne "" ) {
372   open( OUTFILE, ">$outFile" ) or die( "Error ($programName)$bookCodeReport: Unable to open output file \"$outFile\" for writing: $!" );
373   print OUTFILE @lines;
374   close( OUTFILE );
375 }
376 else {
377   print @lines;
378 }
379
380 ################################################################################
381 # Subroutines
382
383 sub validateBookCode {
384     my ($bookCode) = @_;
385
386     # bookCode typically has some space after real data
387     $bookCode =~ s{[[:space:]]+}{}g;
388
389     my %books = (
390         '01fftd' => 1,
391         '02fotw' => 1,
392         '03tcok' => 1,
393         '04tcod' => 1,
394         '05sots' => 1,
395         '06tkot' => 1,
396         '07cd' => 1,
397         '08tjoh' => 1,
398         '09tcof' => 1,
399         '10tdot' => 1,
400         '11tpot' => 1,
401         '12tmod' => 1,
402         '13tplor' => 1,
403         '14tcok' => 1,
404         '15tdc' => 1,
405         '16tlov' => 1,
406         '17tdoi' => 1,
407         '18dotd' => 1,
408         '19wb' => 1,
409         '20tcon' => 1,
410         '21votm' => 1,
411         '22tbos' => 1,
412         '23mh' => 1,
413         '24rw' => 1,
414         '25totw' => 1,
415         '26tfobm' => 1,
416         '27v' => 1,
417         '28thos' => 1,
418         '01gstw' => 1,
419         '02tfc' => 1,
420         '03btng' => 1,
421         '04wotw' => 1,
422         '01hh' => 1,
423         '02smr' => 1,
424         '03toz' => 1,
425         '04cc' => 1,
426         'tmc' => 1,
427         'rh' => 1
428     );
429
430     return exists $books{ $bookCode };
431 }