# Copyright (c) 1997-2006 # Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany) # http://www.math.tu-berlin.de/polymake, mailto:polymake@math.tu-berlin.de # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2, or (at your option) any # later version: http://www.gnu.org/licenses/gpl.txt. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. #---------------------------------------------------------------------------- # $Project: polymake $$Id: File.pm 7370 2006-05-23 21:16:03Z gawrilow $ use strict; use namespaces; require Poly::Application; require Poly::Tempfile; package Poly::File; declare $strict; ################################################################################# # # Constructor: new Poly::File('filename'); # use Struct ( [ new => '$' ], [ '$filename' => '#1' ], '$timestamp', '$line', '@errors', ); ################################################################################# sub need_reload { my ($self)=@_; return (stat $self->filename)[9] > $self->timestamp; } ################################################################################# sub load { my ($self, $object, $file_version)=@_; # FIXME: in XML era it will be recognized from the file contents my $app_name ||= $Poly::User::default_application; $self->timestamp=(stat $self->filename)[9]; open P, $self->filename or die "error reading file '", $self->filename, "': $!\n"; my ($proto); @{$self->errors}=(); # determine the application # first, try at the file begin (since v2.0) while (
) { s/^\s+//; if (/^ _application \s+ ($id_re) \s*$/xo) { $app_name=$1; last; } if (/^ _version \s+ (\S+) \s*$/x) { # it is probably v1.4 $file_version=eval "v$1"; last; } if (substr($_,0,1) eq '#') { next; } if (/\S/) { # try at the file end (v1.5) seek P, -40, 2; local $/; $_=
; if (/^\s* _application \s+ ($id_re) \s*$/xmo) { $app_name=$1; } if (/^\s* _version \s+ (\S+) \s*$/xm) { $file_version=eval "v$1"; } seek P, 0, 0; $.=1; last; } } if (defined($file_version) && $file_version lt v1.5) { $app_name="polytope"; } my $app=add Application($app_name) or return undef; if (!defined $object->name) { my ($object_name)= $self->filename =~ $filename_re; if (defined (my $suffix=$app->file_suffix)) { $object_name =~ s/\.$suffix$//; } $object->name=$object_name; } INPUT: while (
) { s/^\s+//; next if $_ eq ""; if (substr($_,0,1) eq '#') { next; } if (/^(_?dependenci?es)\s*$/i) { while (
) { # skip the DEPENDENCES from old versions last unless /\S/; } } elsif (/^ _version \s+ (\S+) \s*$/x) { $file_version=eval "v$1"; } elsif (/^ _application \s+ ($id_re) \s*$/xo) { next; } elsif (/^ _type \s+ ($type_re) \s*$/xo) { $self->line=$.; unless ($proto=$app->types->{$1}) { $proto=$app->default_type; $self->report_warning("unknown type '$1', default type '", $proto->name, "' assumed"); } bless $object, $proto->object_type; next; } elsif (my ($negated, $prop_name, $attr)=/^(!)? ($id_re) (?(1) | (\([\w+]*\))?) \s*$/xo) { $self->line=$.; if (! $proto) { $proto=$app->default_type; bless $object, $proto->object_type; } my $data=[ ]; while (
) { next if /^\s*\#/; if (/\S/) { push @$data, $_; } else { last; } } enforce_nl($data->[-1]) if $#$data>=0; my ($prop, $unknown)=$proto->property($prop_name); if ($unknown && $strict) { report_error($self, "unknown property '$prop_name' not allowed in strict mode"); } elsif ($prop->temporary) { report_warning($self, "discarding temporary property '$prop_name'"); } else { if ($negated) { if ($file_version le v2.0) { if (@$data) { $self->report_warning("ignoring data in a negated property '!$prop_name'"); } $data= $prop->type eq "boolean" ? [ 0 ] : undef; } else { report_error($self, "obsolete syntax for an undefined property: '!$prop_name'"); undef $data; } } elsif (!@$data) { if ($file_version le v2.0 and $prop->type eq "boolean") { # In v1.3 booleans were encoded as attributes (true) and (false), # while in v1.4 .. v2.0 as empty or negated sections. $data->[0]= $attr ne "(false)"; } } elsif ($file_version gt v2.0 and @$data==1 and $data->[0] =~ $Poly::PropertyValue::UNDEF_re) { undef $data; } if (my $upgrade=$prop->upgrade and $file_version lt $main::VersionNumber) { for (my $i=0; $i<$#$upgrade; $i+=2) { if ($file_version le $upgrade->[$i]) { my @upgraded=eval { select_method($upgrade->[$i+1], $object)->($data) }; if ($@) { report_parse_error($self, "error upgrading property '$prop_name'"); next INPUT; } if (@upgraded==1) { # the property stays the same $data=shift @upgraded; last; } else { # replaced with new properties my $orig_prop_name=$prop_name; while (($prop_name, $data)=splice @upgraded, 0, 2) { ($prop, $unknown)=$proto->property($prop_name); if ($unknown) { if ($strict) { report_error($self, "old property '$orig_prop_name' upgraded to unknown '$prop_name' not allowed in strict mode"); last; } else { report_warning($self, "old property '$orig_prop_name' upgraded to unknown '$prop_name'"); } } eval { $object->add($prop, $data) }; if ($@) { report_parse_error($self, "error upgrading property '$orig_prop_name' to '$prop_name'"); last; } } next INPUT; } } } } eval { $object->add($prop, $data) }; if ($@) { report_parse_error($self, "error reading property '$prop_name'"); } } report_warning($self, "unknown property '$prop_name'") if $unknown and $Switches::v; } else { $self->line=$.; chomp; report_error($self, "ill-formed section header '$_'"); while (
) { # skip the rest of the malformed sektion last unless /\S/; } } } close P; die @{$self->errors} if @{$self->errors}; } ################################################################################# sub report_warning { my $self=shift; warn_print( "\"", $self->filename, "\", line ", $self->line, ": ", @_ ); } ################################################################################# sub report_error { my ($self, $text)=@_; push @{$self->errors}, "\"".$self->filename."\", line ".$self->line.": $text\n"; } ################################################################################# sub report_parse_error { my ($self, $my_text)=@_; my ($where, $text)= $@ =~ /^(?: (.*?) \t)? (.*)/x; $where ||= 0; $where+=$self->line; push @{$self->errors}, "\"".$self->filename."\", line $where: $my_text: $text\n"; } ################################################################################# sub save { my ($self, $object)=@_; my $tempfile=new OverwriteFile($self->filename); open my $tmp, ">", $tempfile or die "can't create temporary file \"$tempfile\" for object ", $object->name, ": $!\n"; my $proto=$object->prototype; print $tmp "_application ", $proto->application->name, "\n", "_version $main::Version\n", "_type ", $proto->name, "\n\n"; foreach my $pv (@{$object->contents}) { next if !defined $pv; print $tmp $pv->property->name, "\n", $pv->toStringArray, "\n" or die "error writing to temporary file '$tempfile': $!\n"; } close $tmp or die "error writing to temporary file '$tempfile': $!\n"; $object->changed=0; } ################################################################################# package Poly::File::Discard; sub new { my $dummy; bless \$dummy, shift } sub load { } sub save { } 1