empserver/info/info.pl
Markus Armbruster 759d3185ad Use strict & warnings. Fix several minor bugs uncovered by it.
Semantics of assigning to $[ changed in Perl 5, and its use is `highly
discouraged'.  Programs obviously assumed $[ affects all arrays, but
it doesn't in Perl 5.  If these programs ever worked, then certainly
not with Perl 5.  Remove the assignments and shift indexes
accordingly.
2004-01-28 21:21:48 +00:00

334 lines
9.7 KiB
Perl

#!/usr/local/bin/perl
#
# info.pl
#
# Create Subjects/Subject.t files from the Info Pages.
#
# written by Ken Stevens
#
#
# DESCRIPTION:
# info.pl reads all of the info pages and creates a table of contents
# for them organized by subject. An info page belongs to a subject if
# that subject appears as an entry in the .SA ("SEE ALSO") field of the
# info page _and_ that entry is not the name of another info page.
#
# For example, the .SA field of headlines.t contains the entries
# "newspaper" and "Communication". Since there's already an info page
# called "newspaper.t", but there is no "Communication" info page, then
# the headlines info page is considered to be a member of the
# Communication subject.
#
# An info page may belong to more than one subject, and if it belongs
# to no subject, then its subject will be set to the name of the subdirectory
# it is in (e.g. the Server and Information info pages work this way).
#
# The output of this script is a bunch of files in the "Subjects"
# subdirectory. The file Subjects/TOP.t is the toplevel table of
# contents and lists all of the subjects. This is what the player
# sees when they type "info". Then for each subject, a
# Subjects/Subject.t file is created, listing all of the info pages that
# belong to that subject.
#
# INSTALLATION:
# info.pl requires perl5 to run. If you don't have version 5 of perl, then
# you'll either have to install it, or you'll have to get someone to create
# your Subjects.t files for you.
#
# HOW TO RUN IT:
# Type "info.pl" at the unix prompt.
#
# BUG REPORTS:
# mail your bug-reports and comments to:
# Ken Stevens <children@empire.net>
# --- Glossary ---
# item.t An info page file
# item An info page
# Subject An entry in a SEE ALSO entry which is not an item
# subdirectory Where the info files are kept
#
# --- Global variables ---
# @dirs Subdirectories of info directory containing item.t files
# @Subjects Subjects which already exist (as Subjects/Subject.t)
# $dir The current subdirectory we're working in
# $filename The name of an item.t file
# $filedir{$filename}
# The subdirectory item.t is in
# F Filehandle for item.t
# $desc{$filename}
# A one line description of the item (second arg to .NA)
# $level{$filename}
# The difficulty level of the page. At present either
# Basic or Expert.
# $see_also{$filename}
# A list of SEE ALSO items for the file (.SA argument)
# $subj A subject
# SUBJ Filehandle for Subject.t
# $subject{$subj}{$dir} = "item1\nitem2\n..."
# Items in that subject organized by directory.
# $largest{$sub} The largest item in that subject (used for column formatting)
# TOP Filehandle for Subjects/TOP.t
# @rowsubj List of subjects
# @colsubj List of subjects organized into 3 columns
#
# --- Functions ---
#
# read_subjects Get list of current subjects
# parse_files Parse all the item.t files in one $dir
# parse_file Check the .TH, .NA, and .SA fields & parse them
# parse_see_also Create %subject from %see_also
# set_subject Add a new entry to %subject and possibly to %largest
# create_subj Create a Subject.t file
# create_subjects Remove the old Subject.t files and create new ones
# flush_subj Print a row of Subjects to TOP
# error Print an integrity error to STDERR and exit with code 1.
use strict;
use warnings;
our (@dirs, $dir, $filename, %filedir, @Subjects, $type, %desc, %level);
our (%see_also, %subject, %largest, $subj, @rowsubj, @colsubj, @subj);
eval("require 5"); # Test for perl version 5
die "info.pl requires version 5 of perl.\n" if $@;
# These are the directories which contain item.t files:
@dirs = ('Commands', 'Concepts', 'Server', 'Introduction');
# Get list of current subjects
&read_subjects;
# Parse the item.t files in each info directory
for $dir (@dirs) {
&parse_files;
}
# Create %subject from %see_also
for $filename (sort keys %filedir) {
&parse_see_also;
}
# Create the Subject.t files
&create_subjects;
exit 0;
# Get list of current subjects
sub read_subjects {
open (LS, "ls Subjects|");
while (<LS>) {
chop;
next unless /^(\S+).t/;
push(@Subjects, $1);
}
close LS;
}
# Parse all the item.t files in one $dir with lots of integrity checks
sub parse_files {
local ($type) = $dir;
chop($type) unless $type eq "Server" || $type eq "Introduction";
if (defined $filedir{$dir}) {
$filename = $dir;
&error("Illegal filename (it is a directory name).");
} elsif (defined $filedir{$type}) {
$filename = $type;
&error("Illegal filename (it is a type name).");
}
open (LS, "cd $dir && ls *.t|");
while (<LS>) {
chop;
$filename = $_;
&parse_file;
}
close LS;
}
# Check the .TH, .NA, and .SA fields.
# Parse .NA into %desc and .SA into %see_also
sub parse_file {
$filename =~ s/\.t$//;
if (grep (/^$filename$/, @dirs)) {
&error("Illegal filename. $filename is a name of a subdirectory of the info directory.");
}
if ($filedir{$filename}) {
&error("$filename.t is in both $filedir{$filename} and $dir");
} elsif (grep (/^$filename$/, @Subjects)) {
&error("Illegal filename. $filename is already a Subject name.");
} else {
$filedir{$filename} = $dir;
}
die "Can't open $dir/$filename.t\n" unless open(F, "<$dir/$filename.t");
$_ = <F>;
if (/^\.TH (\S+) (\S.+\S)$/) {
if ($1 ne $type) {
&error("First argument to .TH was '$1' but it should be '$type'");
}
if ($type eq "Command" && $2 ne "\U$filename") {
&error("Second argument to .TH was '$2' but it should be '\U$filename'");
}
} else {
&error("The first line in the file must be a .TH entry");
}
$_ = <F>;
if (/^\.NA (\S+) "(\S.+\S)"$/) {
if ($filename ne $1) {
&error("First argument to .NA was '$1' but it should be '$filename'");
}
$desc{$filename} = $2;
} else {
&error("The second line in the file must be an .NA entry");
}
$_ = <F>;
if (/^\.LV (\S+)$/) {
if ($1 ne 'Basic' && $1 ne 'Expert') {
&error("The argument to .LV was '$1' but it must be either 'Basic' or 'Expert'");
}
$level{$filename} = $1;
} else {
&error("The third line in the file must be a .LV entry");
}
while (<F>) {
last if /^\.SA/;
}
if ($_) {
if (/^\.SA "([^\"]*)"/) {
$see_also{$filename} = $1;
} else {
&error("Incorrect .SA Syntax. Syntax should be '.SA \"item1, item2\"'");
}
while (<F>) {
&error("Multiple .SA entries. Each file may contain at most one .SA entry") if /^\.SA/;
}
}
close F;
}
# Create %subject from %see_also
sub parse_see_also {
my (@see_also) = split(/, /, $see_also{$filename});
local ($dir) = $filedir{$filename};
my ($found); # Does this item belong to any Subject?
for (@see_also) {
if (!(defined $filedir{$_})) { # is this entry a subject?
&set_subject;
$found = 1;
}
}
&error("No Subject listed in .SA field") unless $found;
}
# Add a new entry to %subject and possibly to %largest
sub set_subject {
$subject{$_}{$dir} .= "$filename\n";
$largest{$_} = "" unless defined $largest{$_};
$largest{$_} = $filename if length $filename > length $largest{$_};
$largest{$_} = $dir if length $dir > length $largest{$_};
}
# Create a Subject.t file
sub create_subj {
print " Creating Subjects/$subj.t\n";
print "WARNING: $subj is a NEW subject\n" unless
grep(/^$subj$/, @Subjects);
die "Unable to write to Subjects/$subj.t\n" unless
open(SUBJ, ">Subjects/$subj.t");
print SUBJ '.\" DO NOT EDIT THIS FILE. It was automatically generated by info.pl'."\n";
print SUBJ ".TH Subject \U$subj\n";
$largest{$subj} =~ s/-/M/g;
print SUBJ ".in \\w'$largest{$subj}XX\\0\\0\\0\\0'u\n";
for $dir (keys %{$subject{$subj}}) {
print SUBJ ".s1\n";
for (split(/\n/, $subject{$subj}{$dir})) {
print SUBJ ".L \"$_ ";
if ($level{$_} eq 'Basic') {
print SUBJ "* \"\n";
} else {
print SUBJ " \"\n";
}
print SUBJ "$desc{$_}\n";
}
}
print SUBJ <<EOF;
.s1
.in 0
For info on a particular subject, type "info <subject>" where <subject> is
one of the subjects listed above. Subjects marked by * are the most
important and should be read by new players.
EOF
close SUBJ;
}
# Remove the old Subject.t files and create the Subject.t files and TOP.t
sub create_subjects {
print " Removing Subjects/*.t\n";
`rm -f Subjects/*.t`;
print " Creating Subjects/TOP.t\n";
die "Can't open Subjects/TOP.t" unless open(TOP, ">Subjects/TOP.t");
print TOP <<EOF;
.TH Info "List of Subjects"
.s1
Empire info is available on the following subjects:
.NF
EOF
@rowsubj = sort keys %subject;
for $subj (@Subjects) {
print "WARNING: The subject $subj has been removed.\n" unless
$subj eq 'TOP' || grep (/^$subj$/, @rowsubj);
}
my $k = 0;
for my $i (0..2) {
for (my $j = $i; $j <= $#rowsubj; $j += 3) {
$colsubj[$j] = $rowsubj[$k++];
}
}
for $subj (@colsubj) {
&create_subj;
push(@subj, $subj);
&flush_subj if $#subj > 1;
}
&flush_subj;
print TOP <<EOF;
.FI
Type "info <Subject>" where <Subject> is one of the subjects listed above.
For a complete list of all info topics, type "info all".
EOF
close TOP;
}
# Print a row of subjects to TOP
sub flush_subj {
return unless $#subj >= 0;
print TOP " ";
for (@subj) {
printf TOP "%-25s", $_;
}
print TOP "\n";
@subj = ();
}
# Print an integrity error message and exit with code 1
sub error {
my ($error) = @_;
print STDERR "Error on line $. of $filedir{$filename}/$filename.t:\n";
print STDERR "$_";
print STDERR "\n" unless /\n$/;
print STDERR "$error\n";
exit 1;
}