Following on from my original message, I've implemented the workaround that I should have thought of on friday, and subclassed Net::LDAP::LDIF, overloading read_lines to remove the relevant loop.
This does still look like a bit of a design inconsistency to me though: having an accessor to the file handle is of limited utility if it can't be manipulated, and the next_lines functionality does make manipulating the file handle iffy, unless I'm missing something. > -----Original Message----- > From: Colbourn, Charles > Sent: 19 May 2006 13:01 > To: 'perl-ldap@perl.org' > Subject: Net::LDAP::LDIF - seeking filehandles > > Hi, > > I'm working on some code that needs to be able to seek in an LDIF file > based on DN. The idea is to iterate through the file, finding each entry > in turn and using tell($ldif->handle) to get the position in the file. > That provides an index to allow something like random read access to the > file. I knocked up this test case to make sure it was feasible: > > # ldif fpos test > use strict; > use warnings; > use Net::LDAP::LDIF; > > my $ldif = Net::LDAP::LDIF->new("dump.ldif","r",onerror=>'die'); > > my $entry = $ldif->read_entry; > my $fpos = tell($ldif->handle); > print "Fpos =$fpos\n"; > $entry = $ldif->read_entry; > > > > print "DN = ".$entry->dn()."\nfpos = ",tell($ldif->handle)."\n\n"; > for (1..100) > { > $entry = $ldif->read_entry; > } > print "Current position = ".tell($ldif->handle)."\n"; > print "DN way down in file = ".$entry->dn."\n"; > > seek($ldif->handle,$fpos,0); > print "fps = ".tell($ldif->handle)."\n"; > $entry = $ldif->read_entry; > print "Sought dn = ".$entry->dn."\n"; > print "fps = ".tell($ldif->handle)."\n"; > > > Unfortunately, although seeking the filehandle back to the chosen DN works > to the extent that the filehandle pointer is in the right place, > $ldif->read_entry returns the entry after the last one read in the 1..100 > loop - i.e. as if the seek hadn't happened. Digging around, it appears > that Net::LDAP::LDIF is caching the next entry in the file as it reads > each entry, in Net::LDAP::LDIF::_read_lines. Each time _read_lines is > called, if the cached next entry exists it returns that, otherwise it > reads the file. If the second do{}until block in this sub is commented out > as follows the test case works: > > sub _read_lines { > my $self = shift; > my @ldif; > { > local $/ = ""; > my $fh = $self->{'fh'}; > my $ln; > do { # allow comments separated by blank lines > $ln = $self->{_next_lines} || scalar <$fh>; > unless ($ln) { > $self->{_next_lines} = ''; > $self->{_current_lines} = ''; > $self->eof(1); > return; > } > $ln =~ s/\n //sg; > $ln =~ s/^#.*\n//mg; > chomp($ln); > $self->{_current_lines} = $ln; > } until ($self->{_current_lines} || $self->eof()); > chomp(@ldif = split(/^/, $ln)); > #do { > # $ln = scalar <$fh> || ''; > # $self->eof(1) unless $ln; > # $ln =~ s/\n //sg; > # $ln =~ s/^#.*\n//mg; > # chomp($ln); > # $self->{_next_lines} = $ln; > #} until ($self->{_next_lines} || $self->eof()); > > } > > @ldif; > } > > > but I'm not sure why the code is caching the next entry, and what > commenting out this block might break; plus of course I don' t really want > to have a customised version of the module on my installation. The > alternative would be to directly remove the private attrib _next_lines, or > to recreate the object before each seek - neither of which strike me as a > good idea. Anyone care to comment? > > cheers, > > Charles Colbourn. > > >