D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
blackhat.pw
:
/
kunden
/
proc
/
self
/
root
/
usr
/
share
/
perl5
/
XBase
/
Filename :
Base.pm
back
Copy
=head1 NAME XBase::Base - Base input output module for XBase suite =cut package XBase::Base; use strict; use IO::File; use Fcntl qw( O_RDWR O_RDONLY O_BINARY ); $XBase::Base::VERSION = '1.02'; # Sets the debug level $XBase::Base::DEBUG = 0; sub DEBUG () { $XBase::Base::DEBUG } my $SEEK_VIA_READ = 0; # Holds the text of the global error, if there was one $XBase::Base::errstr = ''; # Fetch the error message sub errstr () { ( ref $_[0] ? $_[0]->{'errstr'} : $XBase::Base::errstr ); } # Set errstr and print error on STDERR if there is debug level sub Error (@) { my $self = shift; ( ref $self ? $self->{'errstr'} : $XBase::Base::errstr ) = join '', @_; } # Null the errstr sub NullError { shift->Error(''); } # Build the object in the memory, open the file sub new { __PACKAGE__->NullError(); my $class = shift; my $new = bless {}, $class; if (@_ and not $new->open(@_)) { return; } return $new; } # Open the specified file. Use the read_header to load the header data sub open { __PACKAGE__->NullError(); my $self = shift; my %options; if (scalar(@_) % 2) { $options{'name'} = shift; } $self->{'openoptions'} = { %options, @_ } unless defined $self->{'openoptions'}; %options = (%options, @_); if (defined $self->{'fh'}) { $self->close(); } my $external_fh = 0; my $fh = new IO::File; my $rw; if ($options{'name'} eq '-') { if (defined $options{'fh'}) { $fh = $options{'fh'}; $external_fh = 1; } else { $fh->fdopen(fileno(STDIN), 'r'); $self->{'stream'} = 1; SEEK_VIA_READ(1); } $rw = 0; } else { my $ok = 1; if (not $options{'readonly'}) { if ($fh->open($options{'name'}, O_RDWR|O_BINARY)) { $rw = 1; } else { $ok = 0; } } if (not $ok) { if ($fh->open($options{'name'}, O_RDONLY|O_BINARY)) { $rw = 0; $ok = 1; } else { $ok = 0; } } if (not $ok) { __PACKAGE__->Error("Error opening file $options{'name'}: $!\n"); return; } } $self->{'tell'} = 0 if $SEEK_VIA_READ; $fh->autoflush(); binmode($fh) unless $external_fh; @{$self}{ qw( fh filename rw ) } = ($fh, $options{'name'}, $rw); ## $self->locksh(); # read_header should be defined in the derived class $self->read_header(@_); } # Close the file sub close { my $self = shift; $self->NullError(); if (not defined $self->{'fh'}) { $self->Error("Can't close file that is not opened\n"); return; } $self->{'fh'}->close(); delete $self->{'fh'}; 1; } # Read from the filehandle sub read { my $self = shift; my $fh = $self->{'fh'} or return; my $result = $fh->read(@_); if (defined $result and defined $self->{'tell'}) { $self->{'tell'} += $result; } $result; } # Tell the position sub tell { my $self = shift; if (defined $self->{'tell'}) { return $self->{'tell'}; } return $self->{'fh'}->tell(); } # Drop (unlink) the file sub drop { my $self = shift; $self->NullError(); if (defined $self->{'filename'}) { my $filename = $self->{'filename'}; $self->close() if defined $self->{'fh'}; if (not unlink $filename) { $self->Error("Error unlinking file $filename: $!\n"); return; } } 1; } # Create new file sub create_file { my $self = shift; my ($filename, $perms) = @_; if (not defined $filename) { __PACKAGE__->Error("Name has to be specified when creating new file\n"); return; } if (-f $filename) { __PACKAGE__->Error("File $filename already exists\n"); return; } $perms = 0644 unless defined $perms; my $fh = new IO::File; $fh->open($filename, 'w+', $perms) or return; binmode($fh); @{$self}{ qw( fh filename rw ) } = ($fh, $filename, 1); return $self; } # Compute the offset of the record sub get_record_offset { my ($self, $num) = @_; my ($header_len, $record_len) = ($self->{'header_len'}, $self->{'record_len'}); unless (defined $header_len and defined $record_len) { $self->Error("Header and record lengths not known in get_record_offset\n"); return; } unless (defined $num) { $self->Error("Number of the record must be specified in get_record_offset\n"); return; } return $header_len + $num * $record_len; } # Seek to start of the record sub seek_to_record { my ($self, $num) = @_; defined (my $offset = $self->get_record_offset($num)) or return; $self->seek_to($offset); } # Seek to absolute position sub seek_to_seek { my ($self, $offset) = @_; unless (defined $self->{'fh'}) { $self->Error("Cannot seek on unopened file\n"); return; } unless ($self->{'fh'}->seek($offset, 0)) { $self->Error("Seek error (file $self->{'filename'}, offset $offset): $!\n"); return; } 1; } sub seek_to_read { my ($self, $offset) = @_; unless (defined $self->{'fh'}) { $self->Error("Cannot seek on unopened file\n"); return; } my $tell = $self->tell(); if ($offset < $tell) { $self->Error("Cannot seek backwards without using seek ($offset < $tell)\n"); return; } if ($offset > $tell) { my $undef; $self->read($undef, $offset - $tell); $tell = $self->tell(); } if ($tell != $offset) { $self->Error("Some error occured during read-seek: $!\n"); return; } 1; } sub SEEK_VIA_READ { local $^W = 0; if ($_[0]) { *seek_to = \&seek_to_read; $SEEK_VIA_READ = 1; } else { *seek_to = \&seek_to_seek; $SEEK_VIA_READ = 0; } } SEEK_VIA_READ(0); # Read the record of given number. The second parameter is the length of # the record to read. It can be undefined, meaning read the whole record, # and it can be negative, meaning at most the length sub read_record { my ($self, $num, $in_length) = @_; if (not defined $num) { $self->Error("Number of the record must be defined when reading it\n"); return; } if ($self->last_record > 0 and $num > $self->last_record) { $self->Error("Can't read record $num, there is not so many of them\n"); return; } if (not defined $in_length) { $in_length = $self->{'record_len'}; } if ($in_length < 0) { $in_length = -$self->{'record_len'}; } defined (my $offset = $self->get_record_offset($num)) or return; $self->read_from($offset, $in_length); } sub read_from { my ($self, $offset, $in_length) = @_; unless (defined $offset) { $self->Error("Offset to read from must be specified\n"); return; } $self->seek_to($offset) or return; my $length = $in_length; $length = -$length if $length < 0; my $buffer; my $read = $self->read($buffer, $length); if (not defined $read or ($in_length > 0 and $read != $in_length)) { $self->Error("Error reading $in_length bytes from $self->{'filename'}\n"); return; } $buffer; } # Write the given record sub write_record { my ($self, $num) = (shift, shift); defined (my $offset = $self->get_record_offset($num)) or return; defined $self->write_to($offset, @_) or return; $num == 0 ? '0E0' : $num; } # Write data directly to offset sub write_to { my ($self, $offset) = (shift, shift); if (not $self->{'rw'}) { $self->Error("The file $self->{'filename'} is not writable\n"); return; } $self->seek_to($offset) or return; local ($,, $\) = ('', ''); $self->{'fh'}->print(@_) or do { $self->Error("Error writing to offset $offset in file $self->{'filename'}: $!\n"); return; }; $offset == 0 ? '0E0' : $offset; } sub locksh { _locksh(shift->{'fh'}) } sub lockex { _lockex(shift->{'fh'}) } sub unlock { _unlock(shift->{'fh'}) } sub _locksh { flock(shift, 1); } sub _lockex { flock(shift, 2); } sub _unlock { flock(shift, 8); } 1; __END__ =head1 SYNOPSIS Used indirectly, via XBase or XBase::Memo. =head1 DESCRIPTION This module provides catch-all I/O methods for other XBase classes, should be used by people creating additional XBase classes/methods. There is nothing interesting in here for users of the XBase(3) module. Methods in XBase::Base return nothing (undef) on error and the error message can be retrieved using the B<errstr> method. Methods are: =over 4 =item new Constructor. Creates the object and if the file name is specified, opens the file. =item open Opens the file and using method read_header reads the header and sets the object's data structure. The read_header should be defined in the derived class, there is no default. =item close Closes the file, doesn't destroy the object. =item drop Unlinks the file. =item create_file Creates file of given name. Second (optional) parameter is the permission specification for the file. =back The reading/writing methods assume that the file has got header of length header_len bytes (possibly 0) and then records of length record_len. These two values should be set by the read_header method. =over 4 =item seek_to, seek_to_record Seeks to absolute position or to the start of the record. =item read_record, read_from Reads data from specified position (offset) or from the given record. The second parameter (optional for B<read_record>) is the length to read. It can be negative, and at that case the read will not complain if the file is shorter than requested. =item write_to, write_record Writes data to the absolute position or to specified record position. The data is not padded to record_len, just written out. =back General locking methods are B<locksh>, B<lockex> and B<unlock>, they call B<_locksh>, B<_lockex> and B<_unlock> which can be redefined to allow any way for locking (not only the default flock). The user is responsible for calling the lock if he needs it. No more description -- check the source code if you need to know more. =head1 VERSION 1.02 =head1 AVAILABLE FROM http://www.adelton.com/perl/DBD-XBase/ =head1 AUTHOR (c) 1997--2011 Jan Pazdziora. =head1 SEE ALSO perl(1), XBase(3)