]> git.pond.sub.org Git - empserver/blob - info/mksubj.pl
Simplify how mksubj.pl parses argument of .SA
[empserver] / info / mksubj.pl
1 #!/usr/bin/perl
2 #
3 #   Empire - A multi-player, client/server Internet based war game.
4 #   Copyright (C) 1986-2013, Dave Pare, Jeff Bailey, Thomas Ruschak,
5 #                 Ken Stevens, Steve McClure, Markus Armbruster
6 #
7 #   Empire is free software: you can redistribute it and/or modify
8 #   it under the terms of the GNU General Public License as published by
9 #   the Free Software Foundation, either version 3 of the License, or
10 #   (at your option) any later version.
11 #
12 #   This program is distributed in the hope that it will be useful,
13 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #   GNU General Public License for more details.
16 #
17 #   You should have received a copy of the GNU General Public License
18 #   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 #   ---
21 #
22 #   See files README, COPYING and CREDITS in the root of the source
23 #   tree for related information and legal notices.  It is expected
24 #   that future projects/authors will amend these files as needed.
25 #
26 #   ---
27 #
28 #   mksubj.pl: Update the subject index pages
29 #
30 #   Known contributors to this file:
31 #      Ken Stevens (when it was still info.pl)
32 #      Markus Armbruster, 2006-2013
33 #
34 # Usage: mksubj.pl SUBJECT... INFO-FILE...
35 #
36 # Read the INFO-FILE..., update info/SUBJECT.t for each SUBJECT.
37
38 use strict;
39 use warnings;
40 use File::stat;
41
42 # The chapters, in order
43 my @Chapters = qw/Introduction Concept Command Server/;
44
45 my @Levels = qw/Basic Expert Obsolete/;
46 my %Subjects;
47
48 # $filename{TOPIC} is TOPIC's file name
49 my %filename;
50 # $long{TOPIC} is true when TOPIC's page is "long"
51 my %long;
52 # $chapter{TOPIC} is TOPIC's chapter (first arg to .TH)
53 my %chapter;
54 # $desc{TOPIC} is a one line description of TOPIC (second arg to .NA)
55 my %desc;
56 # $level{TOPIC} is TOPIC's difficulty level (arg to .LV)
57 my %level;
58
59 # current info file
60 my $filename;
61
62 # $subject{$subj}{$chap} = "item1\nitem2\n..."
63 #                 Topics in that subject organized by chapter.
64 my %subject;
65 # $largest{$sub}  The largest topic name in that subject (used for
66 #                 column formatting)
67 my %largest;
68
69 while ($#ARGV >= 0 && $ARGV[0] !~ /\.t$/) {
70     $Subjects{shift @ARGV} = undef;
71 }
72
73 for (@ARGV) {
74     $filename{fn2topic($_)} = $_;
75 }
76
77 for (@ARGV) {
78     parse_file($_);
79 }
80
81 for (keys %Subjects) {
82     update_subj($_);
83 }
84
85 sub fn2topic {
86     my ($fn) = @_;
87     $fn =~ s,.*/([^/]*)\.t$,$1,;
88     return $fn;
89 }
90
91 # Parse an info file
92 # Set $filename, $long{TOPIC}, $chapter{TOPIC}, $desc{TOPIC},
93 # $level{TOPIC}.
94 # Update %subject, %largest.
95 sub parse_file {
96     ($filename) = @_;
97     my $topic = fn2topic($filename);
98     my $st;
99
100     $st = stat $filename
101         or die "Can't stat $filename: $!";
102     $long{$topic} = $st->size > 9999;
103
104     open(F, "<$filename")
105         or die "Can't open $filename: $!";
106
107     $_ = <F>;
108     if (/^\.TH (\S+) (\S.+\S)$/) {
109         if (!grep(/^$1$/, @Chapters)) {
110             error("First argument to .TH was '$1', which is not a known chapter");
111         }
112         $chapter{$topic} = $1;
113         if ($1 eq "Command" && $2 ne "\U$topic") {
114             error("Second argument to .TH was '$2' but it should be '\U$topic'");
115         }
116     } else {
117         error("The first line in the file must be a .TH request");
118     }
119
120     $_ = <F>;
121     if (/^\.NA (\S+) "(\S.+\S)"$/) {
122         if ($topic ne $1) {
123             error("First argument to .NA was '$1' but it should be '$topic'");
124         }
125         $desc{$topic} = $2;
126     } else {
127         error("The second line in the file must be a .NA request");
128     }
129
130     $_ = <F>;
131     if (/^\.LV (\S+)$/) {
132         if (!grep(/^$1$/, @Levels)) {
133             error("The argument to .LV was '$1', which is not a known level");
134         }
135         $level{$topic} = $1;
136     } else {
137         error("The third line in the file must be a .LV request");
138     }
139
140     while (<F>) {
141         last if /^\.SA/;
142     }
143
144     if ($_) {
145         if (/^\.SA "([^\"]*)"/) {
146             parse_see_also($topic, $1);
147         } else {
148             error("Incorrect .SA Syntax.  Syntax should be '.SA \"item1, item2\"'");
149         }
150
151         while (<F>) {
152             error("Multiple .SA requests.  Each file may contain at most one.") if /^\.SA/;
153         }
154     } else {
155         error(".SA request is missing");
156     }
157
158     close F;
159 }
160
161 sub parse_see_also {
162     my ($topic, $sa) = @_;
163     my $wanted = $chapter{$topic};
164     my $found;                 # found a subject?
165
166     $wanted = undef if $wanted eq 'Concept' or $wanted eq 'Command';
167
168     for (split(/, /, $sa)) {
169         next if exists $filename{$_};
170         error("Unknown topic $_ in .SA") unless exists $Subjects{$_};
171         set_subject($_, $topic);
172         $found = 1;
173         if ($wanted && $_ eq $wanted) {
174             $wanted = undef;
175         }
176     }
177
178     error("No subject listed in .SA") unless $found;
179     error("Chapter $wanted not listed in .SA") if $wanted;
180 }
181
182 # Add a new entry to %subject and possibly to %largest
183 sub set_subject {
184     my ($sub, $topic) = @_;
185     my $chap = $chapter{$topic};
186     $subject{$sub}{$chap} .= "$topic\n";
187     $largest{$sub} = "" unless defined $largest{$_};
188     $largest{$sub} = $topic if length $topic > length $largest{$sub};
189 }
190
191 # Update a Subject.t file
192 sub update_subj {
193     my ($subj) = @_;
194     my $fname = "info/$subj.t";
195     my $out = "";
196     my ($any_topic, $any_basic, $any_obsolete, $any_long);
197
198     $out .= '.\" DO NOT EDIT THIS FILE.  It was automatically generated by mksubj.pl'."\n";
199     $out .= ".TH Subject \U$subj\n";
200     $largest{$subj} =~ s/-/M/g;
201     $out .= ".in \\w'$largest{$subj}XX\\0\\0\\0\\0'u\n";
202     for my $chap (@Chapters) {
203         next unless exists $subject{$subj}{$chap};
204         $out .= ".s1\n";
205         for my $topic (split(/\n/, $subject{$subj}{$chap})) {
206             $any_topic = 1;
207             my $flags = "";
208             if ($level{$topic} eq 'Basic') {
209                 $flags .= "*";
210                 $any_basic = 1;
211             }
212             if ($level{$topic} eq 'Obsolete') {
213                 $flags .= "+";
214                 $any_obsolete = 1;
215             }
216             if ($long{$topic}) {
217                 $flags .= "!";
218                 $any_long = 1;
219             }
220             $flags = sprintf("%-2s", $flags);
221             $out .= ".L \"$topic $flags\"\n";
222             $out .= "$desc{$topic}\n";
223         }
224     }
225     unless ($any_topic) {
226         print STDERR "$0: Subject $subj has no topics\n";
227         exit 1;
228     }
229     $out .= ".s1\n"
230         . ".in 0\n"
231         . "For info on a particular subject, type \"info <subject>\" where <subject> is\n"
232         . "one of the subjects listed above.\n";
233     $out .= "Subjects marked by * are the most important and should be read by new players.\n"
234         if $any_basic;
235     $out .= "Subjects marked by + are obsolete.\n"
236         if $any_obsolete;
237     $out .= "Unusually long subjects are marked with a !.\n"
238         if $any_long;
239
240     return if (same_contents($fname, $out));
241     open(SUBJ, ">$fname")
242         or die "Can't open $fname for writing: $!";
243     print SUBJ $out;
244     close SUBJ;
245 }
246
247 sub same_contents {
248     my ($fname, $contents) = @_;
249     local $/;
250
251     if (!open(SUBJ, "<$fname")) {
252         return 0 if ($!{ENOENT});
253         die "Can't open $fname for reading: $!";
254     }
255     my $old = <SUBJ>;
256     close SUBJ;
257     return $contents eq $old;
258 }
259
260 # Print an integrity error message and exit with code 1
261 sub error {
262     my ($error) = @_;
263
264     print STDERR "mksubj.pl:$filename:$.: $error\n";
265     exit 1;
266 }