The CmUtils::IpFileFix package provides a simple mechanism for writing scripts to update CMISS ipfiles.
0.3 (18 June 2002)
0.3
GBS: Now gives sensible error message if run not using perl >= 5.6.0
0.2
GBS: Added documentation.
GBS: Made pattern-checking optional for add_before
and add_after
, and
removed it from the replace
.
The current set of scripts (in $CMISS_ROOT/cmiss_utils/filefixes/20010215/) have been rewritten to use this module, and subsequent fixes are most easily copied from these files.
Each script should be named according to the files to be changed (e.g. fixipopti.pl) because the list of files to be changed is automatically generated from the script name. The file suffix is determined by the letters between "fixip" and the next "." or "_", allowing multiple scripts for one file type by including a description that starts with an underscore following the filetype (e.g. fixipopti_10dec99.pl).
The following header should be used to load the module.
#!/usr/local/bin/perl -w use strict; use CmUtils::IpFileFix qw(fixfiles);
The standard interface is through the fixfiles() routine. See below in the /EXPERTS section for lower-level interaction.
This routine takes one named argument called filter which should specify a code reference containing the commands required to "fix" each ipfile. This is most easily defined as an anonymous subroutine. The subroutine is called with one argument, which is a pointer to the file being processed.
fixfiles( filter => sub { my $file = shift; ... } );
Earlier scripts processed the file line-by-line, but this led to confusion and
inefficiencies. The fixfiles
routine reads the entire file before calling
the subroutine defined by filter for processing.
One or more file modification functions describe the actions to take to update
the ipfiles, and are specified in the filter subroutine in the region ...
above.
All functions have a named interface, which means that the arguments may occur in any order. Arguments are typically given as strings, although regular expressions may be used for greater flexibility. In all cases where answers will be included in the match, regular expressions should be used, as they allow for various answers/formats/spaces. Arguments given as strings will be quoted in any pattern matches that are done, and are therefore treated as literals.
Because the file is read into a single multi-line variable, if you want to tie
your regular expression to the start/end of any line, you will need to use the
multi-line (?m)
option:
qr/(?m)^ Enter/
otherwise ^
and $
will only match the start and end of the file.
Alternatively, if you wish to treat the file as one single line (for matching
across more than one line), use the single-line (?s)
option:
qr/(?s)this.*that/
in which case the .
will also match newlines. The \n
character may
always be used to specify a known newline.
$file->add_before(target => "", insert => "" [, unless => ""]);
Adds a string insert before another string/regexp target. Most often, a third string unless needs to be checked so that the addition is not applied several times.
$file->add_before( target => " USE_MINOS (0 or 1)", insert => " USE_MAPS (0 or 1)[1]: 0\n", unless => " USE_MAPS (0 or 1)" );
$file->add_before( target => qr/(?m)^ USE_MINOS/, insert => " USE_MAPS (0 or 1)[1]: 0\n", unless => qr/USE_MAPS/ );
New string to be inserted. Makes no sense for this to be a regexp. Multi-line
strings are most easily defined using <<
.
$file->add_before( target => " Do you want to use parallel element stiffness computations", insert => <<EOF, Specify the convergence criteria [1]: (1) Ratio of unconstrained to constrained residuals (2) Ratio of unconstrained residuals to maximum Gauss point value 1 EOF unless => "Specify the convergence criteria" );
$file->add_after(target => "", insert => "" [, unless => ""]);
Adds a string insert after another string/regexp target, unless a third
string unless is matched. As for add_before
.
Returns the result of attempting to match a given pattern in the file. Most
often, a regular expression should be used, so that varying formatting will
give correct answers. Results may be captured (using ()
) for later use if
necessary.
my ($nodenum) = $file->match(pattern => qr/number of nodes:\s*(\d+)/); my @values = $file->match( pattern => qr/values for C2.*:\s*([\d.]+)\s+([\d.]+)\s+([\d.]+)/ );
my @values = map {split} $file->match(pattern => qr/for C2.*:(.*)\n/);
If the argument is a string, it will do a literal string match:
if ( $file->match(pattern => "number of nodes:\n") ) { ... }
Deletes the first occurence of the given string from the file.
$file->delete(string => " (default 1)"); $file->delete(string => qr/(?m)^ Enter the step limit.*$/);
Global deletion (if ever needed) could be accomplished with:
my $pattern = "something"; while ($file->match(pattern => $pattern)) { $file->delete(string => $pattern); }
Replaces one given string or regexp with another string.
$file->replace(target => "nubmer", replace => "number");
The variable holding the file is actually a blessed hash, with a single entry
{text}
containing the entire file as a single string. Any manipulations
that cannot be done with the provided routines may be performed directly on the
$file->{text}
entry e.g.
$file->{text} =~ tr[A-Z][a-z];
Additionally, files may be read and written directly.
Creates a CmUtils::IpFileFix hash. An optional argument specifies the filename.
$file = new CmUtils::IpFileFix; $file = new CmUtils::IpFileFix ("file.ippara");
Stores a file in the {text}
entry of the file hash.
$file->read($filename);
Writes the {text}
entry in the file hash to an output file.
$file->write("output.ipfile");