#!/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 # --- 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 () { 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 () { 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"); $_ = ; 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"); } $_ = ; 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"); } $_ = ; 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 () { last if /^\.SA/; } if ($_) { if (/^\.SA "([^\"]*)"/) { $see_also{$filename} = $1; } else { &error("Incorrect .SA Syntax. Syntax should be '.SA \"item1, item2\"'"); } while () { &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 <" where 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 < 1; } &flush_subj; print TOP <" where 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; }