package GameConfig; =head1 NAME GameConfig - read, parse, deal with game's bin/ config files =head1 SYNOPSIS use GameConfig; # gm has put $GAME/bin in @INC already my $configfile = "bin/packets.config"; my (%config, %extract); GameConfig::packets_config($configfile, \%config, \%extract); print "$configfile misc settings:\n", map { "\t$_ is set to $config{$_}\n" } keys %config; print "$configfile extractables:\n", map { "\t$_ is type $extract{$_}{'type'}\n" } keys %extract; my $arg = shift; my $field = GameConfig::match_extractable(\%extract, $arg); =head1 DESCRIPTION Config files for the Template perl scripts are kept in bin/ along with the scripts themselves. (We exclude the optional $GAME/.structure here; that is dealt with by the gm script and may reset where bin/ itself is.) For each type of Template config file, GameConfig provides functions for reading and parsing it and stashing the information, and possibly additional functions for common uses of that information. =cut use strict; use English '-no_match_vars'; $WARNING = 1; # same as usual -w option $OUTPUT_AUTOFLUSH = 1; =head2 F bin/packets.config is the usual location for the information desired by the packets script, including extractable field definitions and settings and miscellaneous configuration. The field defns are often needed by other scripts. =over =item B (I, I, I) The given I is game-relative (or gm-tmp-relative) and is taken to be of the form of bin/packets.config and read in. Everything from a % to end of line is considered a comment and cut. The arguments I and I must be references to hashes; these will be filled in with information from the file. Lines of the form foo = bar baz quux are miscellaneous configuration options; "foo" may not contain whitespace but is otherwise arbitrary. Leading/trailing whitespace is cut from "bar baz quux". This line causes the equivalent of $configref->{foo} = "bar baz quux" It is perfectly reasonable for a new script to define some new options of this form, but these should be documented in packets.config to avoid naming collisions. Callers of this subroutine must ignore any I keys they do not recognize; they should not consider their presence a problem. Lines of the form Foo Bar Baz : I I I define extractable field "Foo Bar Baz" (which lives in the $GAME/FooBarBaz directory unless $GAME/.structure redirects). The I must be "list", "tex", or "char". "char" denotes the charsheet type itself (not really an extractable in most ways); exactly one field of this type must be present. I declares what printer this type should be sent to. Anything further on the line is taken as options to pass lpr, usually -Zsomething. This line causes the equivalent of $extractref->{"Foo Bar Baz"} = { "type" => "I", "printer" => "I", "lpropt" => "I", "compress" => "FooBarBaz", }; Thus keys %$extractref will be a list of the fields. If any problems are encountered, packets_config will warn() about them and will die() when done; otherwise it simply returns. =cut sub packets_config { my ($file, $configref, $extractref) = @_; my $fh = GAME::open $file or die "can't read packets config $file\n"; my $configerr = 0; my $ncharfields = 0; local $INPUT_RECORD_SEPARATOR = "\n"; while (<$fh>) { chomp; s/%.*//; next unless /\S/; if (/^\s*(\S+?)\s*=\s*(.*?)\s*$/) { $configref->{$1} = $2; next; } my ($field, $data); unless (($field, $data) = /^\s*(\S.*?)\s*:\s*(\S.*?)\s*$/) { warn "Bad line in $file (doesn't parse):\n\t$_\n"; $configerr = 1; next; } unless (@{$extractref->{$field}}{qw( type printer lpropt )} = $data =~ /^\s*(\S+)\s+(\S+)\s*(.*?)\s*$/) { warn "Bad line in $file (need type and printer):\n\t$_\n"; $configerr = 1; next; } if ((my $type = $extractref->{$field}{type}) eq "char") { ++$ncharfields; } elsif ($type ne "tex" and $type ne "list") { warn "Bad line in $file (type $type not tex/list/char):\n\t$_\n"; $configerr = 1; next; } ($extractref->{$field}{"compress"} = $field) =~ tr/ \t//d; } close $fh or die "Unable to close packets config $file\n"; if ($ncharfields > 1) { warn "Multiple type-char entries (charsheets) in $file\n"; $configerr = 1; } elsif ($ncharfields < 1) { warn "No type-char entry (charsheets) defined in $file\n"; $configerr = 1; } $configerr and die "Bad packets configuration file $file\n"; } =item (string) B (I, string I) I should be a reference to a hash that was initialized by use in B (or constructed by hand to have the resulting form). The I string is a partial field name. If I case-insensitively matches exactly one extractable field name's compressed form (i.e. whitespace removed) of those in I, that field name (uncompressed) is returned. Otherwise, it die()s complaining about either the lack of matching fields or the ambiguity of multiple matches (including the list). =back =cut sub match_extractable { my $extref = shift; my $seek = shift; my @match = grep { $extref->{$_}{compress} =~ /^\Q$seek\E/i } keys %$extref; @match > 1 and die "'$seek' ambiguous: ", (join ' or ', @match), "\n"; @match < 1 and die "no extractable field '$seek' defined\n"; return $match[0]; } # If there were more bin/ config files, we'd do them here, # =head2 usual_file_name file # (description) # =over # for each sub for that file, # =item subroutine prototype # (description) # =back 1;