package App::Cilly::OutputFile; our @ISA = (); use strict; use Carp; use File::Basename; use File::Spec; ######################################################################## my $debug = 0; sub new { croak 'bad argument count' unless @_ == 3; my ($proto, $basis, $filename) = @_; my $class = ref($proto) || $proto; $basis = $basis->basis if ref $basis; my $ref = { filename => $filename, basis => $basis }; my $self = bless $ref, $class; $self->checkRef($filename); $self->checkRef($basis); $self->checkProtected(); $self->checkTemporary(); Carp::cluck "OutputFile: filename == $filename, basis == $basis" if $debug; return $self; } sub filename { my ($self) = @_; return $self->{filename}; } sub basis { my ($self) = @_; return $self->{basis}; } ######################################################################## sub checkRef { my ($self, $filename) = @_; confess "ref found where string expected: $filename" if ref $filename; confess "stringified ref found where string expected: $filename" if $filename =~ /\w+=HASH\(0x[0-9a-f]+\)/; } sub checkTemporary { my ($self) = @_; my ($basename, $path) = fileparse $self->filename; return if $path eq File::Spec->tmpdir . '/'; confess "found temporary file in wrong directory: ", $self->filename if $basename =~ /^cil-[a-zA-Z0-9]{8}\./; } ######################################################################## my @protected = (); sub checkProtected { my ($self) = @_; my $abs = File::Spec->rel2abs($self->filename); foreach (@protected) { confess "caught attempt to overwrite protected file: ", $self->filename if $_ eq $abs; } } sub protect { my ($self, @precious) = @_; push @protected, File::Spec->rel2abs($_) foreach @precious; } ######################################################################## 1; __END__ =head1 Name OutputFile - base class for intermediate compiler output files =head1 Description C represents an intermediate output file generated by some stage of a C-based compiler. This is an abstract base class and should never be instantiated directly. It provides common behaviors used by concrete subclasses L and L. =head2 Public Methods =over =item filename An C instance is a smart wrapper around a file name. C<< $out->filename >> returns the name of the file represented by C instance C<$out>. When building a command line, this is the string to use for the file. For example: my $out = ... ; # some OutputFile subclass my @argv = ('gcc', '-E', '-o', $out->filename, 'input.c'); system @argv; C often creates command vectors with a mix of strings and C objects. This is fine, but before using a mixed vector as a command line, you must replace all C objects with their corresponding file names: my @mixed = (...); # mix of strings and objects my @normalized = @mixed; $_ = (ref $_ ? $_->filename : $_) foreach @normalized; system @normalized; Common utility methods like C already do exactly this normalization, but you may need to do it yourself if you are running external commands on your own. =item protect C contains safety interlocks that help it avoid stomping on user input files. C<< OutputFile->protect($precious) >> marks C<$precious> as a protected input file which should not be overwritten. If any C tries to claim this same file name, an error will be raised. In theory, this never happens. In practice, scripts can have bugs, and it's better to be safe than sorry. C uses this method to register input files that it discovers during command line processing. If you add special command line processing of your own, or if you identify input files through other means, we highly recommend using this method as well. Otherwise, there is some risk that a buggy client script could mistakenly create an output file that destroys the user's source code. Note that C is a class method: call it on the C module, rather than on a specific instance. =back =head2 Internal Methods The following methods are used within C or by C subclasses. They are not intended for use by outside scripts. =over =item basis In addition to L, each C instance records a second file name: its I. The basis file name is initialized and used differently by different subclasses, but typically represents the input file from which this output file is derived. C<< $out->basis >> returns the basis file name for instance C<$out>. When instantiating an C, the caller can provide either a file name string as the basis or another C instance. However, basis file names are not chained: if C<< $a->basis >> is F, and C<$b> is constructed with C<$a> as its basis, C<< $b->basis >> will return F, not C<$a> or C<< $a->filename >>. This flattening is done at construction time. See L and L for more details on how basis file names are used. =item checkRef C<< OutputFile->checkRef($filename) >> raises an error if C<$filename> is an object reference, or looks like the string representation of an object reference. Used to sanity check arguments to various methods. =item checkTemporary C<< $out->checkTemporary >> raises an error if C<< $out->filename >> looks like a temporary file name but is not in the system temporary directory. Used to sanity check arguments in various methods. =item checkProtected C<< $out->checkProtected >> raises an error if C<< $out->filename >> is listed as a protected file. This check, performed at construction time, implements a safety interlock to prevent overwriting of user input files. Protected files are registered using L<"protect">. =back =head1 See Also L, L. =cut